#include #include #include #include #include #ifdef Q_OS_UNIX #include #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 { Q_OBJECT public: ClientTest() { sock = new TQSocket; connect(sock, SIGNAL(connected()), SLOT(sock_connected())); connect(sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); connect(sock, SIGNAL(error(int)), SLOT(sock_error(int))); sasl = new QCA::SASL; connect(sasl, SIGNAL(clientFirstStep(const TQString &, const TQByteArray *)), SLOT(sasl_clientFirstStep(const TQString &, const TQByteArray *))); connect(sasl, SIGNAL(nextStep(const TQByteArray &)), SLOT(sasl_nextStep(const TQByteArray &))); connect(sasl, SIGNAL(needParams(bool, bool, bool, bool)), SLOT(sasl_needParams(bool, bool, bool, bool))); connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); connect(sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int))); connect(sasl, SIGNAL(error(int)), 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 { Q_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); TTQTimer::singleShot(0, this, SIGNAL(quit())); return; } char myhostname[256]; int r = gethostname(myhostname, sizeof(myhostname)-1); if(r == -1) { printf("Error getting hostname!\n"); TTQTimer::singleShot(0, this, 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, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); connect(sock, SIGNAL(error(int)), SLOT(sock_error(int))); connect(sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int))); sasl = new QCA::SASL; connect(sasl, SIGNAL(authCheck(const TQString &, const TQString &)), SLOT(sasl_authCheck(const TQString &, const TQString &))); connect(sasl, SIGNAL(nextStep(const TQByteArray &)), SLOT(sasl_nextStep(const TQByteArray &))); connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); connect(sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int))); connect(sasl, SIGNAL(error(int)), 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, SIGNAL(quit()), &app, SLOT(quit())); s->start(); app.exec(); delete s; } else { ClientTest *c = new ClientTest; TQObject::connect(c, SIGNAL(quit()), &app, SLOT(quit())); c->start(host, PROTO_PORT, user, pass); app.exec(); delete c; } return 0; }