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/groupwise/libgroupwise/qca/src/qca.cpp

1487 lines
26 KiB

/*
* qca.cpp - Qt Cryptographic Architecture
* Copyright (C) 2003 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
*
*/
#include"qca.h"
#include<qptrlist.h>
#include<qdir.h>
#include<qfileinfo.h>
#include<qstringlist.h>
#include<qlibrary.h>
#include<qtimer.h>
#include<qhostaddress.h>
#include<qapplication.h>
#include<qguardedptr.h>
#include<stdlib.h>
#include"qcaprovider.h"
#if defined(Q_OS_WIN32)
#define PLUGIN_EXT "dll"
#elif defined(Q_OS_MAC)
#define PLUGIN_EXT "dylib"
#else
#define PLUGIN_EXT "so"
#endif
using namespace QCA;
class ProviderItem
{
public:
QCAProvider *p;
QString fname;
static ProviderItem *load(const QString &fname)
{
QLibrary *lib = new QLibrary(fname);
if(!lib->load()) {
delete lib;
return 0;
}
void *s = lib->resolve("createProvider");
if(!s) {
delete lib;
return 0;
}
QCAProvider *(*createProvider)() = (QCAProvider *(*)())s;
QCAProvider *p = createProvider();
if(!p) {
delete lib;
return 0;
}
ProviderItem *i = new ProviderItem(lib, p);
i->fname = fname;
return i;
}
static ProviderItem *fromClass(QCAProvider *p)
{
ProviderItem *i = new ProviderItem(0, p);
return i;
}
~ProviderItem()
{
delete p;
delete lib;
}
void ensureInit()
{
if(init_done)
return;
init_done = true;
p->init();
}
private:
QLibrary *lib;
bool init_done;
ProviderItem(QLibrary *_lib, QCAProvider *_p)
{
lib = _lib;
p = _p;
init_done = false;
}
};
static QPtrList<ProviderItem> providerList;
static bool qca_init = false;
static bool plugin_have(const QString &fname)
{
QPtrListIterator<ProviderItem> it(providerList);
for(ProviderItem *i; (i = it.current()); ++it) {
if(i->fname == fname)
return true;
}
return false;
}
static void plugin_scan()
{
QStringList dirs = QApplication::libraryPaths();
for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
QDir libpath(*it);
QDir dir(libpath.filePath("crypto"));
if(!dir.exists())
continue;
QStringList list = dir.entryList();
for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
QFileInfo fi(dir.filePath(*it));
if(fi.isDir())
continue;
if(fi.extension() != PLUGIN_EXT)
continue;
QString fname = fi.filePath();
// don't load the same plugin again!
if(plugin_have(fname))
continue;
//printf("f=[%s]\n", fname.latin1());
ProviderItem *i = ProviderItem::load(fname);
if(!i)
continue;
if(i->p->qcaVersion() != QCA_PLUGIN_VERSION) {
delete i;
continue;
}
providerList.append(i);
}
}
}
static void plugin_addClass(QCAProvider *p)
{
ProviderItem *i = ProviderItem::fromClass(p);
providerList.prepend(i);
}
static void plugin_unloadall()
{
providerList.clear();
}
static int plugin_caps()
{
int caps = 0;
QPtrListIterator<ProviderItem> it(providerList);
for(ProviderItem *i; (i = it.current()); ++it)
caps |= i->p->capabilities();
return caps;
}
QString QCA::arrayToHex(const QByteArray &a)
{
QString out;
for(int n = 0; n < (int)a.size(); ++n) {
QString str;
str.sprintf("%02x", (uchar)a[n]);
out.append(str);
}
return out;
}
QByteArray QCA::hexToArray(const QString &str)
{
QByteArray out(str.length() / 2);
int at = 0;
for(int n = 0; n + 1 < (int)str.length(); n += 2) {
uchar a = str[n];
uchar b = str[n+1];
uchar c = ((a & 0x0f) << 4) + (b & 0x0f);
out[at++] = c;
}
return out;
}
void QCA::init()
{
if(qca_init)
return;
qca_init = true;
providerList.setAutoDelete(true);
}
bool QCA::isSupported(int capabilities)
{
init();
int caps = plugin_caps();
if(caps & capabilities)
return true;
// ok, try scanning for new stuff
plugin_scan();
caps = plugin_caps();
if(caps & capabilities)
return true;
return false;
}
void QCA::insertProvider(QCAProvider *p)
{
plugin_addClass(p);
}
void QCA::unloadAllPlugins()
{
plugin_unloadall();
}
static void *getContext(int cap)
{
init();
// this call will also trip a scan for new plugins if needed
if(!QCA::isSupported(cap))
return 0;
QPtrListIterator<ProviderItem> it(providerList);
for(ProviderItem *i; (i = it.current()); ++it) {
if(i->p->capabilities() & cap) {
i->ensureInit();
return i->p->context(cap);
}
}
return 0;
}
//----------------------------------------------------------------------------
// Hash
//----------------------------------------------------------------------------
class Hash::Private
{
public:
Private()
{
c = 0;
}
~Private()
{
delete c;
}
void reset()
{
c->reset();
}
QCA_HashContext *c;
};
Hash::Hash(QCA_HashContext *c)
{
d = new Private;
d->c = c;
}
Hash::Hash(const Hash &from)
{
d = new Private;
*this = from;
}
Hash & Hash::operator=(const Hash &from)
{
delete d->c;
d->c = from.d->c->clone();
return *this;
}
Hash::~Hash()
{
delete d;
}
void Hash::clear()
{
d->reset();
}
void Hash::update(const QByteArray &a)
{
d->c->update(a.data(), a.size());
}
QByteArray Hash::final()
{
QByteArray buf;
d->c->final(&buf);
return buf;
}
//----------------------------------------------------------------------------
// Cipher
//----------------------------------------------------------------------------
class Cipher::Private
{
public:
Private()
{
c = 0;
}
~Private()
{
delete c;
}
void reset()
{
dir = Encrypt;
key.resize(0);
iv.resize(0);
err = false;
}
QCA_CipherContext *c;
int dir;
int mode;
QByteArray key, iv;
bool err;
};
Cipher::Cipher(QCA_CipherContext *c, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
{
d = new Private;
d->c = c;
reset(dir, mode, key, iv, pad);
}
Cipher::Cipher(const Cipher &from)
{
d = new Private;
*this = from;
}
Cipher & Cipher::operator=(const Cipher &from)
{
delete d->c;
d->c = from.d->c->clone();
d->dir = from.d->dir;
d->mode = from.d->mode;
d->key = from.d->key.copy();
d->iv = from.d->iv.copy();
d->err = from.d->err;
return *this;
}
Cipher::~Cipher()
{
delete d;
}
QByteArray Cipher::dyn_generateKey(int size) const
{
QByteArray buf;
if(size != -1)
buf.resize(size);
else
buf.resize(d->c->keySize());
if(!d->c->generateKey(buf.data(), size))
return QByteArray();
return buf;
}
QByteArray Cipher::dyn_generateIV() const
{
QByteArray buf(d->c->blockSize());
if(!d->c->generateIV(buf.data()))
return QByteArray();
return buf;
}
void Cipher::reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
{
d->reset();
d->dir = dir;
d->mode = mode;
d->key = key.copy();
d->iv = iv.copy();
if(!d->c->setup(d->dir, d->mode, d->key.isEmpty() ? 0: d->key.data(), d->key.size(), d->iv.isEmpty() ? 0 : d->iv.data(), pad)) {
d->err = true;
return;
}
}
bool Cipher::update(const QByteArray &a)
{
if(d->err)
return false;
if(!a.isEmpty()) {
if(!d->c->update(a.data(), a.size())) {
d->err = true;
return false;
}
}
return true;
}
QByteArray Cipher::final(bool *ok)
{
if(ok)
*ok = false;
if(d->err)
return QByteArray();
QByteArray out;
if(!d->c->final(&out)) {
d->err = true;
return QByteArray();
}
if(ok)
*ok = true;
return out;
}
//----------------------------------------------------------------------------
// SHA1
//----------------------------------------------------------------------------
SHA1::SHA1()
:Hash((QCA_HashContext *)getContext(CAP_SHA1))
{
}
//----------------------------------------------------------------------------
// SHA256
//----------------------------------------------------------------------------
SHA256::SHA256()
:Hash((QCA_HashContext *)getContext(CAP_SHA256))
{
}
//----------------------------------------------------------------------------
// MD5
//----------------------------------------------------------------------------
MD5::MD5()
:Hash((QCA_HashContext *)getContext(CAP_MD5))
{
}
//----------------------------------------------------------------------------
// BlowFish
//----------------------------------------------------------------------------
BlowFish::BlowFish(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
:Cipher((QCA_CipherContext *)getContext(CAP_BlowFish), dir, mode, key, iv, pad)
{
}
//----------------------------------------------------------------------------
// TripleDES
//----------------------------------------------------------------------------
TripleDES::TripleDES(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
:Cipher((QCA_CipherContext *)getContext(CAP_TripleDES), dir, mode, key, iv, pad)
{
}
//----------------------------------------------------------------------------
// AES128
//----------------------------------------------------------------------------
AES128::AES128(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
:Cipher((QCA_CipherContext *)getContext(CAP_AES128), dir, mode, key, iv, pad)
{
}
//----------------------------------------------------------------------------
// AES256
//----------------------------------------------------------------------------
AES256::AES256(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
:Cipher((QCA_CipherContext *)getContext(CAP_AES256), dir, mode, key, iv, pad)
{
}
//----------------------------------------------------------------------------
// RSAKey
//----------------------------------------------------------------------------
class RSAKey::Private
{
public:
Private()
{
c = 0;
}
~Private()
{
delete c;
}
QCA_RSAKeyContext *c;
};
RSAKey::RSAKey()
{
d = new Private;
d->c = (QCA_RSAKeyContext *)getContext(CAP_RSA);
}
RSAKey::RSAKey(const RSAKey &from)
{
d = new Private;
*this = from;
}
RSAKey & RSAKey::operator=(const RSAKey &from)
{
delete d->c;
d->c = from.d->c->clone();
return *this;
}
RSAKey::~RSAKey()
{
delete d;
}
bool RSAKey::isNull() const
{
return d->c->isNull();
}
bool RSAKey::havePublic() const
{
return d->c->havePublic();
}
bool RSAKey::havePrivate() const
{
return d->c->havePrivate();
}
QByteArray RSAKey::toDER(bool publicOnly) const
{
QByteArray out;
if(!d->c->toDER(&out, publicOnly))
return QByteArray();
return out;
}
bool RSAKey::fromDER(const QByteArray &a)
{
return d->c->createFromDER(a.data(), a.size());
}
QString RSAKey::toPEM(bool publicOnly) const
{
QByteArray out;
if(!d->c->toPEM(&out, publicOnly))
return QByteArray();
QCString cs;
cs.resize(out.size()+1);
memcpy(cs.data(), out.data(), out.size());
return QString::fromLatin1(cs);
}
bool RSAKey::fromPEM(const QString &str)
{
QCString cs = str.latin1();
QByteArray a(cs.length());
memcpy(a.data(), cs.data(), a.size());
return d->c->createFromPEM(a.data(), a.size());
}
bool RSAKey::fromNative(void *p)
{
return d->c->createFromNative(p);
}
bool RSAKey::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const
{
QByteArray out;
if(!d->c->encrypt(a, &out, oaep))
return false;
*b = out;
return true;
}
bool RSAKey::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const
{
QByteArray out;
if(!d->c->decrypt(a, &out, oaep))
return false;
*b = out;
return true;
}
bool RSAKey::generate(unsigned int bits)
{
return d->c->generate(bits);
}
//----------------------------------------------------------------------------
// RSA
//----------------------------------------------------------------------------
RSA::RSA()
{
}
RSA::~RSA()
{
}
RSAKey RSA::key() const
{
return v_key;
}
void RSA::setKey(const RSAKey &k)
{
v_key = k;
}
bool RSA::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const
{
if(v_key.isNull())
return false;
return v_key.encrypt(a, b, oaep);
}
bool RSA::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const
{
if(v_key.isNull())
return false;
return v_key.decrypt(a, b, oaep);
}
RSAKey RSA::generateKey(unsigned int bits)
{
RSAKey k;
k.generate(bits);
return k;
}
//----------------------------------------------------------------------------
// Cert
//----------------------------------------------------------------------------
class Cert::Private
{
public:
Private()
{
c = 0;
}
~Private()
{
delete c;
}
QCA_CertContext *c;
};
Cert::Cert()
{
d = new Private;
// crash because this is returning 0
d->c = (QCA_CertContext *)getContext(CAP_X509);
}
Cert::Cert(const Cert &from)
{
d = new Private;
*this = from;
}
Cert & Cert::operator=(const Cert &from)
{
delete d->c;
if ( from.d->c )
d->c = from.d->c->clone();
else
d->c = 0;
return *this;
}
Cert::~Cert()
{
delete d;
}
void Cert::fromContext(QCA_CertContext *ctx)
{
delete d->c;
d->c = ctx;
}
bool Cert::isNull() const
{
return d->c->isNull();
}
QString Cert::commonName() const
{
CertProperties props = subject();
return props["CN"];
}
QString Cert::serialNumber() const
{
return d->c->serialNumber();
}
QString Cert::subjectString() const
{
return d->c->subjectString();
}
QString Cert::issuerString() const
{
return d->c->issuerString();
}
CertProperties Cert::subject() const
{
QValueList<QCA_CertProperty> list = d->c->subject();
CertProperties props;
for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
props[(*it).var] = (*it).val;
return props;
}
CertProperties Cert::issuer() const
{
QValueList<QCA_CertProperty> list = d->c->issuer();
CertProperties props;
for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
props[(*it).var] = (*it).val;
return props;
}
QDateTime Cert::notBefore() const
{
return d->c->notBefore();
}
QDateTime Cert::notAfter() const
{
return d->c->notAfter();
}
QByteArray Cert::toDER() const
{
QByteArray out;
if(!d->c->toDER(&out))
return QByteArray();
return out;
}
bool Cert::fromDER(const QByteArray &a)
{
return d->c->createFromDER(a.data(), a.size());
}
QString Cert::toPEM() const
{
QByteArray out;
if(!d->c->toPEM(&out))
return QByteArray();
QCString cs;
cs.resize(out.size()+1);
memcpy(cs.data(), out.data(), out.size());
return QString::fromLatin1(cs);
}
bool Cert::fromPEM(const QString &str)
{
QCString cs = str.latin1();
QByteArray a(cs.length());
memcpy(a.data(), cs.data(), a.size());
return d->c->createFromPEM(a.data(), a.size());
}
//----------------------------------------------------------------------------
// TLS
//----------------------------------------------------------------------------
class TLS::Private
{
public:
Private()
{
c = (QCA_TLSContext *)getContext(CAP_TLS);
}
~Private()
{
delete c;
}
void reset()
{
handshaken = false;
closing = false;
in.resize(0);
out.resize(0);
from_net.resize(0);
to_net.resize(0);
host = "";
hostMismatch = false;
// this causes the crash, because the Cert ctor is setting a null context
cert = Cert();
bytesEncoded = 0;
tryMore = false;
}
void appendArray(QByteArray *a, const QByteArray &b)
{
int oldsize = a->size();
a->resize(oldsize + b.size());
memcpy(a->data() + oldsize, b.data(), b.size());
}
Cert cert;
QCA_TLSContext *c;
QByteArray in, out, to_net, from_net;
int bytesEncoded;
bool tryMore;
bool handshaken;
QString host;
bool hostMismatch;
bool closing;
Cert ourCert;
RSAKey ourKey;
QPtrList<QCA_CertContext> store;
};
TLS::TLS(QObject *parent)
:QObject(parent)
{
d = new Private;
}
TLS::~TLS()
{
delete d;
}
void TLS::setCertificate(const Cert &cert, const RSAKey &key)
{
d->ourCert = cert;
d->ourKey = key;
}
void TLS::setCertificateStore(const QPtrList<Cert> &store)
{
// convert the cert list into a context list
d->store.clear();
QPtrListIterator<Cert> it(store);
for(Cert *cert; (cert = it.current()); ++it)
d->store.append(cert->d->c);
}
void TLS::reset()
{
d->reset();
}
bool TLS::startClient(const QString &host)
{
d->reset();
d->host = host;
if(!d->c->startClient(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
return false;
QTimer::singleShot(0, this, SLOT(update()));
return true;
}
bool TLS::startServer()
{
d->reset();
if(!d->c->startServer(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
return false;
QTimer::singleShot(0, this, SLOT(update()));
return true;
}
void TLS::close()
{
if(!d->handshaken || d->closing)
return;
d->closing = true;
QTimer::singleShot(0, this, SLOT(update()));
}
bool TLS::isHandshaken() const
{
return d->handshaken;
}
void TLS::write(const QByteArray &a)
{
d->appendArray(&d->out, a);
update();
}
QByteArray TLS::read()
{
QByteArray a = d->in.copy();
d->in.resize(0);
return a;
}
void TLS::writeIncoming(const QByteArray &a)
{
d->appendArray(&d->from_net, a);
update();
}
QByteArray TLS::readOutgoing()
{
QByteArray a = d->to_net.copy();
d->to_net.resize(0);
return a;
}
QByteArray TLS::readUnprocessed()
{
QByteArray a = d->from_net.copy();
d->from_net.resize(0);
return a;
}
const Cert & TLS::peerCertificate() const
{
return d->cert;
}
int TLS::certificateValidityResult() const
{
if(d->hostMismatch)
return QCA::TLS::HostMismatch;
else
return d->c->validityResult();
}
void TLS::update()
{
bool force_read = false;
bool eof = false;
bool done = false;
QGuardedPtr<TLS> self = this;
if(d->closing) {
QByteArray a;
int r = d->c->shutdown(d->from_net, &a);
d->from_net.resize(0);
if(r == QCA_TLSContext::Error) {
reset();
error(ErrHandshake);
return;
}
if(r == QCA_TLSContext::Success) {
d->from_net = d->c->unprocessed().copy();
done = true;
}
d->appendArray(&d->to_net, a);
}
else {
if(!d->handshaken) {
QByteArray a;
int r = d->c->handshake(d->from_net, &a);
d->from_net.resize(0);
if(r == QCA_TLSContext::Error) {
reset();
error(ErrHandshake);
return;
}
d->appendArray(&d->to_net, a);
if(r == QCA_TLSContext::Success) {
QCA_CertContext *cc = d->c->peerCertificate();
if(cc && !d->host.isEmpty() && d->c->validityResult() == QCA::TLS::Valid) {
if(!cc->matchesAddress(d->host))
d->hostMismatch = true;
}
d->cert.fromContext(cc);
d->handshaken = true;
handshaken();
if(!self)
return;
// there is a teeny tiny possibility that incoming data awaits. let us get it.
force_read = true;
}
}
if(d->handshaken) {
if(!d->out.isEmpty() || d->tryMore) {
d->tryMore = false;
QByteArray a;
int enc;
bool more = false;
bool ok = d->c->encode(d->out, &a, &enc);
eof = d->c->eof();
if(ok && enc < (int)d->out.size())
more = true;
d->out.resize(0);
if(!eof) {
if(!ok) {
reset();
error(ErrCrypt);
return;
}
d->bytesEncoded += enc;
if(more)
d->tryMore = true;
d->appendArray(&d->to_net, a);
}
}
if(!d->from_net.isEmpty() || force_read) {
QByteArray a, b;
bool ok = d->c->decode(d->from_net, &a, &b);
eof = d->c->eof();
d->from_net.resize(0);
if(!ok) {
reset();
error(ErrCrypt);
return;
}
d->appendArray(&d->in, a);
d->appendArray(&d->to_net, b);
}
if(!d->in.isEmpty()) {
readyRead();
if(!self)
return;
}
}
}
if(!d->to_net.isEmpty()) {
int bytes = d->bytesEncoded;
d->bytesEncoded = 0;
readyReadOutgoing(bytes);
if(!self)
return;
}
if(eof) {
close();
if(!self)
return;
return;
}
if(d->closing && done) {
reset();
closed();
}
}
//----------------------------------------------------------------------------
// SASL
//----------------------------------------------------------------------------
QString saslappname = "qca";
class SASL::Private
{
public:
Private()
{
c = (QCA_SASLContext *)getContext(CAP_SASL);
}
~Private()
{
delete c;
}
void setSecurityProps()
{
c->setSecurityProps(noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual, ssfmin, ssfmax, ext_authid, ext_ssf);
}
// security opts
bool noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual;
int ssfmin, ssfmax;
QString ext_authid;
int ext_ssf;
bool tried;
QCA_SASLContext *c;
QHostAddress localAddr, remoteAddr;
int localPort, remotePort;
QByteArray stepData;
bool allowCSF;
bool first, server;
QByteArray inbuf, outbuf;
};
SASL::SASL(QObject *parent)
:QObject(parent)
{
d = new Private;
reset();
}
SASL::~SASL()
{
delete d;
}
void SASL::setAppName(const QString &name)
{
saslappname = name;
}
void SASL::reset()
{
d->localPort = -1;
d->remotePort = -1;
d->noPlain = false;
d->noActive = false;
d->noDict = false;
d->noAnon = false;
d->reqForward = false;
d->reqCreds = false;
d->reqMutual = false;
d->ssfmin = 0;
d->ssfmax = 0;
d->ext_authid = "";
d->ext_ssf = 0;
d->inbuf.resize(0);
d->outbuf.resize(0);
d->c->reset();
}
int SASL::errorCondition() const
{
return d->c->errorCond();
}
void SASL::setAllowPlain(bool b)
{
d->noPlain = !b;
}
void SASL::setAllowAnonymous(bool b)
{
d->noAnon = !b;
}
void SASL::setAllowActiveVulnerable(bool b)
{
d->noActive = !b;
}
void SASL::setAllowDictionaryVulnerable(bool b)
{
d->noDict = !b;
}
void SASL::setRequireForwardSecrecy(bool b)
{
d->reqForward = b;
}
void SASL::setRequirePassCredentials(bool b)
{
d->reqCreds = b;
}
void SASL::setRequireMutualAuth(bool b)
{
d->reqMutual = b;
}
void SASL::setMinimumSSF(int x)
{
d->ssfmin = x;
}
void SASL::setMaximumSSF(int x)
{
d->ssfmax = x;
}
void SASL::setExternalAuthID(const QString &authid)
{
d->ext_authid = authid;
}
void SASL::setExternalSSF(int x)
{
d->ext_ssf = x;
}
void SASL::setLocalAddr(const QHostAddress &addr, Q_UINT16 port)
{
d->localAddr = addr;
d->localPort = port;
}
void SASL::setRemoteAddr(const QHostAddress &addr, Q_UINT16 port)
{
d->remoteAddr = addr;
d->remotePort = port;
}
bool SASL::startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst)
{
QCA_SASLHostPort la, ra;
if(d->localPort != -1) {
la.addr = d->localAddr;
la.port = d->localPort;
}
if(d->remotePort != -1) {
ra.addr = d->remoteAddr;
ra.port = d->remotePort;
}
d->allowCSF = allowClientSendFirst;
d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
d->setSecurityProps();
if(!d->c->clientStart(mechlist))
return false;
d->first = true;
d->server = false;
d->tried = false;
QTimer::singleShot(0, this, SLOT(tryAgain()));
return true;
}
bool SASL::startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist)
{
QCA_SASLHostPort la, ra;
if(d->localPort != -1) {
la.addr = d->localAddr;
la.port = d->localPort;
}
if(d->remotePort != -1) {
ra.addr = d->remoteAddr;
ra.port = d->remotePort;
}
d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
d->setSecurityProps();
if(!d->c->serverStart(realm, mechlist, saslappname))
return false;
d->first = true;
d->server = true;
d->tried = false;
return true;
}
void SASL::putServerFirstStep(const QString &mech)
{
int r = d->c->serverFirstStep(mech, 0);
handleServerFirstStep(r);
}
void SASL::putServerFirstStep(const QString &mech, const QByteArray &clientInit)
{
int r = d->c->serverFirstStep(mech, &clientInit);
handleServerFirstStep(r);
}
void SASL::handleServerFirstStep(int r)
{
if(r == QCA_SASLContext::Success)
authenticated();
else if(r == QCA_SASLContext::Continue)
nextStep(d->c->result());
else if(r == QCA_SASLContext::AuthCheck)
tryAgain();
else
error(ErrAuth);
}
void SASL::putStep(const QByteArray &stepData)
{
d->stepData = stepData.copy();
tryAgain();
}
void SASL::setUsername(const QString &user)
{
d->c->setClientParams(&user, 0, 0, 0);
}
void SASL::setAuthzid(const QString &authzid)
{
d->c->setClientParams(0, &authzid, 0, 0);
}
void SASL::setPassword(const QString &pass)
{
d->c->setClientParams(0, 0, &pass, 0);
}
void SASL::setRealm(const QString &realm)
{
d->c->setClientParams(0, 0, 0, &realm);
}
void SASL::continueAfterParams()
{
tryAgain();
}
void SASL::continueAfterAuthCheck()
{
tryAgain();
}
void SASL::tryAgain()
{
int r;
if(d->server) {
if(!d->tried) {
r = d->c->nextStep(d->stepData);
d->tried = true;
}
else {
r = d->c->tryAgain();
}
if(r == QCA_SASLContext::Error) {
error(ErrAuth);
return;
}
else if(r == QCA_SASLContext::Continue) {
d->tried = false;
nextStep(d->c->result());
return;
}
else if(r == QCA_SASLContext::AuthCheck) {
authCheck(d->c->username(), d->c->authzid());
return;
}
}
else {
if(d->first) {
if(!d->tried) {
r = d->c->clientFirstStep(d->allowCSF);
d->tried = true;
}
else
r = d->c->tryAgain();
if(r == QCA_SASLContext::Error) {
error(ErrAuth);
return;
}
else if(r == QCA_SASLContext::NeedParams) {
//d->tried = false;
QCA_SASLNeedParams np = d->c->clientParamsNeeded();
needParams(np.user, np.authzid, np.pass, np.realm);
return;
}
QString mech = d->c->mech();
const QByteArray *clientInit = d->c->clientInit();
d->first = false;
d->tried = false;
clientFirstStep(mech, clientInit);
}
else {
if(!d->tried) {
r = d->c->nextStep(d->stepData);
d->tried = true;
}
else
r = d->c->tryAgain();
if(r == QCA_SASLContext::Error) {
error(ErrAuth);
return;
}
else if(r == QCA_SASLContext::NeedParams) {
//d->tried = false;
QCA_SASLNeedParams np = d->c->clientParamsNeeded();
needParams(np.user, np.authzid, np.pass, np.realm);
return;
}
d->tried = false;
//else if(r == QCA_SASLContext::Continue) {
nextStep(d->c->result());
// return;
//}
}
}
if(r == QCA_SASLContext::Success)
authenticated();
else if(r == QCA_SASLContext::Error)
error(ErrAuth);
}
int SASL::ssf() const
{
return d->c->security();
}
void SASL::write(const QByteArray &a)
{
QByteArray b;
if(!d->c->encode(a, &b)) {
error(ErrCrypt);
return;
}
int oldsize = d->outbuf.size();
d->outbuf.resize(oldsize + b.size());
memcpy(d->outbuf.data() + oldsize, b.data(), b.size());
readyReadOutgoing(a.size());
}
QByteArray SASL::read()
{
QByteArray a = d->inbuf.copy();
d->inbuf.resize(0);
return a;
}
void SASL::writeIncoming(const QByteArray &a)
{
QByteArray b;
if(!d->c->decode(a, &b)) {
error(ErrCrypt);
return;
}
int oldsize = d->inbuf.size();
d->inbuf.resize(oldsize + b.size());
memcpy(d->inbuf.data() + oldsize, b.data(), b.size());
readyRead();
}
QByteArray SASL::readOutgoing()
{
QByteArray a = d->outbuf.copy();
d->outbuf.resize(0);
return a;
}
#include "qca.moc"