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.
tqca/examples/sasltest/sasltest.cpp

616 lines
13 KiB

#include<tqapplication.h>
#include<tqtimer.h>
#include<tqsocket.h>
#include<tqserversocket.h>
#include<stdio.h>
#ifdef Q_OS_UNIX
#include<unistd.h>
#endif
#include"base64.h"
#include"qca.h"
#define PROTO_NAME "foo"
#define PROTO_PORT 8001
static TQString prompt(const TQString &s)
{
printf("* %s ", s.latin1());
fflush(stdout);
char line[256];
fgets(line, 255, stdin);
TQString result = line;
if(result[result.length()-1] == '\n')
result.truncate(result.length()-1);
return result;
}
class ClientTest : public TQObject
{
TQ_OBJECT
public:
ClientTest()
{
sock = new TQSocket;
connect(sock, TQ_SIGNAL(connected()), TQ_SLOT(sock_connected()));
connect(sock, TQ_SIGNAL(connectionClosed()), TQ_SLOT(sock_connectionClosed()));
connect(sock, TQ_SIGNAL(readyRead()), TQ_SLOT(sock_readyRead()));
connect(sock, TQ_SIGNAL(error(int)), TQ_SLOT(sock_error(int)));
sasl = new QCA::SASL;
connect(sasl, TQ_SIGNAL(clientFirstStep(const TQString &, const TQByteArray *)), TQ_SLOT(sasl_clientFirstStep(const TQString &, const TQByteArray *)));
connect(sasl, TQ_SIGNAL(nextStep(const TQByteArray &)), TQ_SLOT(sasl_nextStep(const TQByteArray &)));
connect(sasl, TQ_SIGNAL(needParams(bool, bool, bool, bool)), TQ_SLOT(sasl_needParams(bool, bool, bool, bool)));
connect(sasl, TQ_SIGNAL(authenticated()), TQ_SLOT(sasl_authenticated()));
connect(sasl, TQ_SIGNAL(readyRead()), TQ_SLOT(sasl_readyRead()));
connect(sasl, TQ_SIGNAL(readyReadOutgoing(int)), TQ_SLOT(sasl_readyReadOutgoing(int)));
connect(sasl, TQ_SIGNAL(error(int)), TQ_SLOT(sasl_error(int)));
}
~ClientTest()
{
delete sock;
delete sasl;
}
void start(const TQString &_host, int port, const TQString &user="", const TQString &pass="")
{
mode = 0;
host = _host;
sock->connectToHost(host, port);
sasl->setMinimumSSF(0);
sasl->setMaximumSSF(256);
if(!user.isEmpty()) {
sasl->setUsername(user);
sasl->setAuthzid(user);
}
if(!pass.isEmpty())
sasl->setPassword(pass);
}
signals:
void quit();
private slots:
void sock_connected()
{
printf("Connected to server. Awaiting mechanism list...\n");
}
void sock_connectionClosed()
{
printf("Connection closed by peer.\n");
quit();
}
void sock_error(int x)
{
TQString s;
if(x == TQSocket::ErrConnectionRefused)
s = "connection refused or timed out";
else if(x == TQSocket::ErrHostNotFound)
s = "host not found";
else if(x == TQSocket::ErrSocketRead)
s = "read error";
printf("Socket error: %s\n", s.latin1());
quit();
}
void sock_readyRead()
{
if(mode == 2) {
int avail = sock->bytesAvailable();
TQByteArray a(avail);
int n = sock->readBlock(a.data(), a.size());
a.resize(n);
printf("Read %d bytes\n", a.size());
sasl->writeIncoming(a);
}
else {
if(sock->canReadLine()) {
TQString line = sock->readLine();
line.truncate(line.length()-1); // chop the newline
handleLine(line);
}
}
}
void sasl_clientFirstStep(const TQString &mech, const TQByteArray *clientInit)
{
printf("Choosing mech: %s\n", mech.latin1());
TQString line = mech;
if(clientInit) {
TQCString cs(clientInit->data(), clientInit->size()+1);
line += ' ';
line += cs;
}
sendLine(line);
}
void sasl_nextStep(const TQByteArray &stepData)
{
TQCString cs(stepData.data(), stepData.size()+1);
TQString line = "C";
if(!stepData.isEmpty()) {
line += ',';
line += cs;
}
sendLine(line);
}
void sasl_needParams(bool user, bool authzid, bool pass, bool realm)
{
TQString username;
if(user || authzid)
username = prompt("Username:");
if(user) {
sasl->setUsername(username);
}
if(authzid) {
sasl->setAuthzid(username);
}
if(pass) {
sasl->setPassword(prompt("Password (not hidden!) :"));
}
if(realm) {
sasl->setRealm(prompt("Realm:"));
}
sasl->continueAfterParams();
}
void sasl_authenticated()
{
printf("SASL success!\n");
printf("SSF: %d\n", sasl->ssf());
}
void sasl_readyRead()
{
TQByteArray a = sasl->read();
int oldsize = inbuf.size();
inbuf.resize(oldsize + a.size());
memcpy(inbuf.data() + oldsize, a.data(), a.size());
processInbuf();
}
void sasl_readyReadOutgoing(int)
{
TQByteArray a = sasl->readOutgoing();
sock->writeBlock(a.data(), a.size());
}
void sasl_error(int)
{
printf("SASL error!\n");
quit();
return;
}
private:
TQSocket *sock;
QCA::SASL *sasl;
int mode;
TQString host;
TQByteArray inbuf;
void processInbuf()
{
TQStringList list;
for(int n = 0; n < (int)inbuf.size(); ++n) {
if(inbuf[n] == '\n') {
TQCString cs(inbuf.data(), n+1);
char *p = inbuf.data();
++n;
int x = inbuf.size() - n;
memmove(p, p + n, x);
inbuf.resize(x);
list += TQString::fromUtf8(cs);
// start over, basically
n = -1;
}
}
for(TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
handleLine(*it);
}
void handleLine(const TQString &line)
{
printf("Reading: [%s]\n", line.latin1());
if(mode == 0) {
// first line is the method list
TQStringList mechlist = TQStringList::split(' ', line);
++mode;
// kick off the client
sasl->setAllowAnonymous(false);
if(!sasl->startClient(PROTO_NAME, host, mechlist)) {
printf("Error starting client!\n");
quit();
}
}
else if(mode == 1) {
TQString type, rest;
int n = line.find(',');
if(n != -1) {
type = line.mid(0, n);
rest = line.mid(n+1);
}
else {
type = line;
rest = "";
}
if(type == "C") {
TQCString cs = rest.latin1();
TQByteArray buf(cs.length());
memcpy(buf.data(), cs.data(), buf.size());
sasl->putStep(buf);
}
else if(type == "E") {
printf("Authentication failed.\n");
quit();
return;
}
else if(type == "A") {
printf("Authentication success.\n");
++mode;
sock_readyRead(); // any extra data?
return;
}
else {
printf("Bad format from peer, closing.\n");
quit();
return;
}
}
else {
}
}
void sendLine(const TQString &line)
{
printf("Writing: {%s}\n", line.latin1());
TQString s = line + '\n';
TQCString cs = s.latin1();
if(mode == 2) {
TQByteArray a(cs.length());
memcpy(a.data(), cs.data(), a.size());
sasl->write(a);
}
else
sock->writeBlock(cs.data(), cs.length());
}
};
class ServerTest : public QServerSocket
{
TQ_OBJECT
public:
ServerTest(const TQString &_str, int _port) : QServerSocket(_port), port(_port)
{
sock = 0;
sasl = 0;
realm = TQString::null;
str = _str;
}
~ServerTest()
{
delete sock;
delete sasl;
}
void start()
{
if(!ok()) {
printf("Error binding to port %d!\n", port);
TQTimer::singleShot(0, this, TQ_SIGNAL(quit()));
return;
}
char myhostname[256];
int r = gethostname(myhostname, sizeof(myhostname)-1);
if(r == -1) {
printf("Error getting hostname!\n");
TQTimer::singleShot(0, this, TQ_SIGNAL(quit()));
return;
}
host = myhostname;
printf("Listening on %s:%d ...\n", host.latin1(), port);
}
void newConnection(int s)
{
// Note: only 1 connection supported at a time in this example!
if(sock) {
TQSocket tmp;
tmp.setSocket(s);
printf("Connection ignored, already have one active.\n");
return;
}
printf("Connection received! Starting SASL handshake...\n");
sock = new TQSocket;
connect(sock, TQ_SIGNAL(connectionClosed()), TQ_SLOT(sock_connectionClosed()));
connect(sock, TQ_SIGNAL(readyRead()), TQ_SLOT(sock_readyRead()));
connect(sock, TQ_SIGNAL(error(int)), TQ_SLOT(sock_error(int)));
connect(sock, TQ_SIGNAL(bytesWritten(int)), TQ_SLOT(sock_bytesWritten(int)));
sasl = new QCA::SASL;
connect(sasl, TQ_SIGNAL(authCheck(const TQString &, const TQString &)), TQ_SLOT(sasl_authCheck(const TQString &, const TQString &)));
connect(sasl, TQ_SIGNAL(nextStep(const TQByteArray &)), TQ_SLOT(sasl_nextStep(const TQByteArray &)));
connect(sasl, TQ_SIGNAL(authenticated()), TQ_SLOT(sasl_authenticated()));
connect(sasl, TQ_SIGNAL(readyRead()), TQ_SLOT(sasl_readyRead()));
connect(sasl, TQ_SIGNAL(readyReadOutgoing(int)), TQ_SLOT(sasl_readyReadOutgoing(int)));
connect(sasl, TQ_SIGNAL(error(int)), TQ_SLOT(sasl_error(int)));
sock->setSocket(s);
mode = 0;
inbuf.resize(0);
sasl->setMinimumSSF(0);
sasl->setMaximumSSF(256);
TQStringList mechlist;
if(!sasl->startServer(PROTO_NAME, host, realm, &mechlist)) {
printf("Error starting server!\n");
quit();
}
TQString str;
bool first = true;
for(TQStringList::ConstIterator it = mechlist.begin(); it != mechlist.end(); ++it) {
if(!first)
str += ' ';
str += *it;
first = false;
}
sendLine(str);
}
signals:
void quit();
private slots:
void sock_connectionClosed()
{
printf("Connection closed by peer.\n");
close();
}
void sock_error(int x)
{
TQString s;
if(x == TQSocket::ErrConnectionRefused)
s = "connection refused or timed out";
else if(x == TQSocket::ErrHostNotFound)
s = "host not found";
else if(x == TQSocket::ErrSocketRead)
s = "read error";
printf("Socket error: %s\n", s.latin1());
close();
}
void sock_readyRead()
{
if(sock->canReadLine()) {
TQString line = sock->readLine();
line.truncate(line.length()-1); // chop the newline
handleLine(line);
}
}
void sock_bytesWritten(int x)
{
if(mode == 2) {
toWrite -= x;
if(toWrite <= 0) {
printf("Sent, closing.\n");
close();
}
}
}
void sasl_nextStep(const TQByteArray &stepData)
{
TQCString cs(stepData.data(), stepData.size()+1);
TQString line = "C";
if(!stepData.isEmpty()) {
line += ',';
line += cs;
}
sendLine(line);
}
void sasl_authCheck(const TQString &user, const TQString &authzid)
{
printf("AuthCheck: User: [%s], Authzid: [%s]\n", user.latin1(), authzid.latin1());
sasl->continueAfterAuthCheck();
}
void sasl_authenticated()
{
sendLine("A");
printf("Authentication success.\n");
++mode;
printf("SSF: %d\n", sasl->ssf());
sendLine(str);
}
void sasl_readyRead()
{
TQByteArray a = sasl->read();
int oldsize = inbuf.size();
inbuf.resize(oldsize + a.size());
memcpy(inbuf.data() + oldsize, a.data(), a.size());
processInbuf();
}
void sasl_readyReadOutgoing(int)
{
TQByteArray a = sasl->readOutgoing();
toWrite = a.size();
sock->writeBlock(a.data(), a.size());
}
void sasl_error(int x)
{
if(x == QCA::SASL::ErrAuth) {
sendLine("E");
printf("Authentication failed.\n");
close();
}
else {
printf("SASL security layer error!\n");
close();
}
}
private:
TQSocket *sock;
QCA::SASL *sasl;
TQString host, realm;
int port;
int mode;
TQString str;
TQByteArray inbuf;
int toWrite;
void processInbuf()
{
}
void handleLine(const TQString &line)
{
printf("Reading: [%s]\n", line.latin1());
if(mode == 0) {
int n = line.find(' ');
if(n != -1) {
TQString mech = line.mid(0, n);
TQCString cs = line.mid(n+1).latin1();
TQByteArray clientInit(cs.length());
memcpy(clientInit.data(), cs.data(), clientInit.size());
sasl->putServerFirstStep(mech, clientInit);
}
else
sasl->putServerFirstStep(line);
++mode;
}
else if(mode == 1) {
TQString type, rest;
int n = line.find(',');
if(n != -1) {
type = line.mid(0, n);
rest = line.mid(n+1);
}
else {
type = line;
rest = "";
}
if(type == "C") {
TQCString cs = rest.latin1();
TQByteArray buf(cs.length());
memcpy(buf.data(), cs.data(), buf.size());
sasl->putStep(buf);
}
else {
printf("Bad format from peer, closing.\n");
close();
return;
}
}
}
void sendLine(const TQString &line)
{
printf("Writing: {%s}\n", line.latin1());
TQString s = line + '\n';
TQCString cs = s.latin1();
if(mode == 2) {
TQByteArray a(cs.length());
memcpy(a.data(), cs.data(), a.size());
sasl->write(a);
}
else
sock->writeBlock(cs.data(), cs.length());
}
void close()
{
sock->deleteLater();
sock = 0;
delete sasl;
sasl = 0;
}
};
#include"sasltest.moc"
void usage()
{
printf("usage: sasltest client [host] [user] [pass]\n");
printf(" sasltest server [string]\n\n");
}
int main(int argc, char **argv)
{
TQApplication app(argc, argv, false);
TQString host, user, pass;
TQString str = "Hello, World";
bool server;
if(argc < 2) {
usage();
return 0;
}
TQString arg = argv[1];
if(arg == "client") {
if(argc < 3) {
usage();
return 0;
}
host = argv[2];
if(argc >= 4)
user = argv[3];
if(argc >= 5)
pass = argv[4];
server = false;
}
else if(arg == "server") {
if(argc >= 3)
str = argv[2];
server = true;
}
else {
usage();
return 0;
}
if(!QCA::isSupported(QCA::CAP_SASL)) {
printf("SASL not supported!\n");
return 1;
}
if(server) {
ServerTest *s = new ServerTest(str, PROTO_PORT);
TQObject::connect(s, TQ_SIGNAL(quit()), &app, TQ_SLOT(quit()));
s->start();
app.exec();
delete s;
}
else {
ClientTest *c = new ClientTest;
TQObject::connect(c, TQ_SIGNAL(quit()), &app, TQ_SLOT(quit()));
c->start(host, PROTO_PORT, user, pass);
app.exec();
delete c;
}
return 0;
}