The code below shows how to create a SASL client.
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <cstdio>
#include <QtCrypto>
#ifdef QT_STATICPLUGIN
#include "import_plugins.h"
#endif
static QString prompt(const QString &s)
{
printf("* %s ", qPrintable(s));
fflush(stdout);
char line[256];
fgets(line, 255, stdin);
QString result = QString::fromLatin1(line);
if (result[result.length() - 1] == QLatin1Char('\n'))
result.truncate(result.length() - 1);
return result;
}
static QString socketErrorToString(QAbstractSocket::SocketError x)
{
QString s;
switch (x) {
case QAbstractSocket::ConnectionRefusedError:
s = QStringLiteral("connection refused or timed out");
break;
case QAbstractSocket::RemoteHostClosedError:
s = QStringLiteral("remote host closed the connection");
break;
case QAbstractSocket::HostNotFoundError:
s = QStringLiteral("host not found");
break;
case QAbstractSocket::SocketAccessError:
s = QStringLiteral("access error");
break;
case QAbstractSocket::SocketResourceError:
s = QStringLiteral("too many sockets");
break;
case QAbstractSocket::SocketTimeoutError:
s = QStringLiteral("operation timed out");
break;
case QAbstractSocket::DatagramTooLargeError:
s = QStringLiteral("datagram was larger than system limit");
break;
case QAbstractSocket::NetworkError:
s = QStringLiteral("network error");
break;
case QAbstractSocket::AddressInUseError:
s = QStringLiteral("address is already in use");
break;
case QAbstractSocket::SocketAddressNotAvailableError:
s = QStringLiteral("address does not belong to the host");
break;
case QAbstractSocket::UnsupportedSocketOperationError:
s = QStringLiteral("operation is not supported by the local operating system");
break;
default:
s = QStringLiteral("unknown socket error");
break;
}
return s;
}
{
QString s;
switch (x) {
s = QStringLiteral("no appropriate mechanism could be negotiated");
break;
s = QStringLiteral("bad SASL protocol");
break;
s = QStringLiteral("server failed mutual authentication");
break;
default:
s = QStringLiteral("generic authentication failure");
break;
};
return s;
}
{
Q_OBJECT
private:
QString host, proto, authzid, realm, user, pass;
int port;
bool no_authzid, no_realm;
int mode;
QTcpSocket *sock;
QByteArray inbuf;
bool sock_done;
int waitCycles;
public:
ClientTest(const QString &_host,
int _port,
const QString &_proto,
const QString &_authzid,
const QString &_realm,
const QString &_user,
const QString &_pass,
bool _no_authzid,
bool _no_realm)
: host(_host)
, proto(_proto)
, authzid(_authzid)
, realm(_realm)
, user(_user)
, pass(_pass)
, port(_port)
, no_authzid(_no_authzid)
, no_realm(_no_realm)
, sock_done(false)
, waitCycles(0)
{
sock = new QTcpSocket(this);
connect(sock, &QTcpSocket::connected, this, &ClientTest::sock_connected);
connect(sock, &QTcpSocket::readyRead, this, &ClientTest::sock_readyRead);
connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &ClientTest::sock_error);
}
public Q_SLOTS:
void start()
{
mode = 0;
int flags = 0;
flags |= QCA::SASL::AllowPlain;
flags |= QCA::SASL::AllowAnonymous;
if (!user.isEmpty())
if (!authzid.isEmpty())
if (!pass.isEmpty())
if (!realm.isEmpty())
printf("Connecting to %s:%d, for protocol %s\n", qPrintable(host), port, qPrintable(proto));
sock->connectToHost(host, port);
}
Q_SIGNALS:
void quit();
private Q_SLOTS:
void sock_connected()
{
printf("Connected to server. Awaiting mechanism list...\n");
}
void sock_error(QAbstractSocket::SocketError x)
{
if (x == QAbstractSocket::RemoteHostClosedError) {
if (mode == 2)
{
sock_done = true;
tryFinished();
return;
} else
{
printf("Error: server closed connection unexpectedly.\n");
emit quit();
return;
}
}
printf("Error: socket: %s\n", qPrintable(socketErrorToString(x)));
emit quit();
}
void sock_readyRead()
{
if (mode == 2)
{
QByteArray a = sock->readAll();
printf("Read %d bytes\n", a.size());
if (waitCycles == 0) {
waitCycles = 3;
QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection);
}
} else
{
if (sock->canReadLine()) {
QString line = QString::fromLatin1(sock->readLine());
line.truncate(line.length() - 1);
handleLine(line);
}
}
}
void sasl_clientFirstStep(bool clientInit, const QByteArray &clientInitData)
{
printf(
"Choosing mech: %s\n", qPrintable(sasl->
mechanism()));
if (clientInit) {
line += QLatin1Char(' ');
line += arrayToString(clientInitData);
}
sendLine(line);
}
void sasl_nextStep(const QByteArray &stepData)
{
QString line = QStringLiteral("C");
if (!stepData.isEmpty()) {
line += QLatin1Char(',');
line += arrayToString(stepData);
}
sendLine(line);
}
{
user = prompt(QStringLiteral("Username:"));
}
authzid = prompt(QStringLiteral("Authorize As (enter to skip):"));
if (!authzid.isEmpty())
}
prompt.
getHidden(QStringLiteral(
"* Password"));
}
printf("Available realms:\n");
if (realms.isEmpty())
printf(" (none specified)\n");
foreach (const QString &s, realms)
printf(" %s\n", qPrintable(s));
realm = prompt(QStringLiteral("Realm (enter to skip):"));
if (!realm.isEmpty())
}
}
void sasl_authenticated()
{
printf("SASL success!\n");
printf(
"SSF: %d\n", sasl->
ssf());
}
void sasl_readyRead()
{
QByteArray a = sasl->
read();
inbuf += a;
processInbuf();
}
void sasl_readyReadOutgoing()
{
sock->write(a);
}
void sasl_error()
{
printf("Error: sasl: initialization failed.\n");
printf(
"Error: sasl: %s.\n", qPrintable(saslAuthConditionToString(sasl->
authCondition())));
printf("Error: sasl: broken security layer.\n");
else
printf("Error: sasl: unknown error.\n");
emit quit();
}
void waitWriteIncoming()
{
--waitCycles;
if (waitCycles > 0) {
QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection);
return;
}
tryFinished();
}
private:
void tryFinished()
{
if (sock_done && waitCycles == 0) {
printf("Finished, server closed connection.\n");
sasl->disconnect(this);
emit quit();
}
}
QString arrayToString(const QByteArray &ba)
{
}
QByteArray stringToArray(const QString &s)
{
}
void sendLine(const QString &line)
{
printf("Writing: {%s}\n", qPrintable(line));
QString s = line + QLatin1Char('\n');
QByteArray a = s.toUtf8();
if (mode == 2)
else
sock->write(a);
}
void processInbuf()
{
QStringList list;
int at;
while ((at = inbuf.indexOf('\n')) != -1) {
list += QString::fromUtf8(inbuf.mid(0, at));
inbuf = inbuf.mid(at + 1);
}
foreach (const QString &line, list)
handleLine(line);
}
void handleLine(const QString &line)
{
printf("Reading: [%s]\n", qPrintable(line));
if (mode == 0) {
const QStringList mechlist = line.split(QLatin1Char(' '));
mode = 1;
} else if (mode == 1) {
QString type, rest;
int n = line.indexOf(QLatin1Char(','));
if (n != -1) {
type = line.mid(0, n);
rest = line.mid(n + 1);
} else
type = line;
if (type == QLatin1String("C")) {
sasl->
putStep(stringToArray(rest));
} else if (type == QLatin1String("E")) {
if (!rest.isEmpty())
printf("Error: server says: %s.\n", qPrintable(rest));
else
printf("Error: server error, unspecified.\n");
emit quit();
return;
} else if (type == QLatin1String("A")) {
printf("Authentication success.\n");
mode = 2;
sock_readyRead();
return;
} else {
printf("Error: Bad format from peer, closing.\n");
emit quit();
return;
}
}
}
};
void usage()
{
printf("usage: saslclient (options) host(:port) (user) (pass)\n");
printf("options: --proto=x, --authzid=x, --realm=x\n");
}
int main(int argc, char **argv)
{
QCoreApplication qapp(argc, argv);
QStringList args = qapp.arguments();
args.removeFirst();
QString proto = QStringLiteral("qcatest");
QString authzid, realm;
bool no_authzid = false;
bool no_realm = false;
for (int n = 0; n < args.count(); ++n) {
if (!args[n].startsWith(QLatin1String("--")))
continue;
QString opt = args[n].mid(2);
QString var, val;
int at = opt.indexOf(QLatin1Char('='));
if (at != -1) {
var = opt.mid(0, at);
val = opt.mid(at + 1);
} else
var = opt;
if (var == QLatin1String("proto")) {
proto = val;
} else if (var == QLatin1String("authzid")) {
if (val.isEmpty())
no_authzid = true;
else
authzid = val;
} else if (var == QLatin1String("realm")) {
if (val.isEmpty())
no_realm = true;
else
realm = val;
}
args.removeAt(n);
--n;
}
if (args.count() < 1) {
usage();
return 0;
}
QString host, user, pass;
int port = 8001;
QString hostinput = args[0];
if (args.count() >= 2)
user = args[1];
if (args.count() >= 3)
pass = args[2];
int at = hostinput.indexOf(QLatin1Char(':'));
if (at != -1) {
host = hostinput.mid(0, at);
port = hostinput.midRef(at + 1).toInt();
} else
host = hostinput;
printf("Error: SASL support not found.\n");
return 1;
}
ClientTest client(host, port, proto, authzid, realm, user, pass, no_authzid, no_realm);
QObject::connect(&client, &ClientTest::quit, &qapp, &QCoreApplication::quit);
QTimer::singleShot(0, &client, &ClientTest::start);
qapp.exec();
return 0;
}
#include "saslclient.moc"