sasltest.cpp

The code below shows how to create a SASL client.

00001 /*
00002  Copyright (C) 2003-2006 Justin Karneges <justin@affinix.com>, Michail Pishchagin
00003 
00004  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  of this software and associated documentation files (the "Software"), to deal
00006  in the Software without restriction, including without limitation the rights
00007  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  copies of the Software, and to permit persons to whom the Software is
00009  furnished to do so, subject to the following conditions:
00010 
00011  The above copyright notice and this permission notice shall be included in
00012  all copies or substantial portions of the Software.
00013 
00014  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00017  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00018  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00019  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00020 */
00021 
00022 #include <QCoreApplication>
00023 #include <QTimer>
00024 #include <QTcpSocket>
00025 #include <QTcpServer>
00026 #include <stdio.h>
00027 
00028 #ifdef Q_OS_UNIX
00029 #include <unistd.h>
00030 #endif
00031 
00032 // QtCrypto has the declarations for all of QCA
00033 #include <QtCrypto>
00034 
00035 #define PROTO_NAME "foo"
00036 #define PROTO_PORT 8001
00037 
00038 static QString prompt(const QString &s)
00039 {
00040     printf("* %s ", s.toLatin1().data());
00041     fflush(stdout);
00042     char line[256];
00043     fgets(line, 255, stdin);
00044     QString result = line;
00045     if(result[result.length()-1] == '\n')
00046         result.truncate(result.length()-1);
00047     return result;
00048 }
00049 
00050 class ClientTest : public QObject
00051 {
00052     Q_OBJECT
00053 public:
00054     ClientTest()
00055     {
00056         sock = new QTcpSocket;
00057         connect(sock, SIGNAL(connected()), SLOT(sock_connected()));
00058         connect(sock, SIGNAL(disconnected()), SLOT(sock_connectionClosed()));
00059         connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
00060         connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError)));
00061 
00062         sasl = new QCA::SASL;
00063         connect(sasl, SIGNAL(clientStarted(bool, const QByteArray &)), SLOT(sasl_clientFirstStep(bool, const QByteArray &)));
00064         connect(sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
00065         connect(sasl, SIGNAL(needParams(const QCA::SASL::Params &)), SLOT(sasl_needParams(const QCA::SASL::Params &)));
00066         connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
00067         connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
00068         connect(sasl, SIGNAL(readyReadOutgoing()), SLOT(sasl_readyReadOutgoing()));
00069         connect(sasl, SIGNAL(error()), SLOT(sasl_error()));
00070     }
00071 
00072     void start(const QString &_host, int port, const QString &user="", const QString &pass="")
00073     {
00074         mode = 0;
00075         host = _host;
00076         sock->connectToHost(host, port);
00077         sasl->setConstraints((QCA::SASL::AuthFlags)(QCA::SASL::AllowPlain | QCA::SASL::AllowAnonymous), 0, 256);
00078 
00079         if(!user.isEmpty()) {
00080             sasl->setUsername(user);
00081             sasl->setAuthzid(user);
00082         }
00083         if(!pass.isEmpty())
00084             sasl->setPassword(pass.toUtf8());
00085     }
00086 
00087     ~ClientTest()
00088     {
00089         delete sock;
00090         delete sasl;
00091     }
00092 
00093 signals:
00094     void quit();
00095 
00096 private slots:
00097     void sock_connected()
00098     {
00099         printf("Connected to server.  Awaiting mechanism list...\n");
00100     }
00101 
00102     void sock_connectionClosed()
00103     {
00104         printf("Connection closed by peer.\n");
00105         quit();
00106     }
00107 
00108     void sock_error(QAbstractSocket::SocketError x)
00109     {
00110         printSocketError(x);
00111         quit();
00112     }
00113 
00114     void sock_readyRead()
00115     {
00116         if(mode == 2) {
00117             int avail = sock->bytesAvailable();
00118             QByteArray a(avail, 0);
00119             int n = sock->read(a.data(), a.size());
00120             a.resize(n);
00121             printf("Read %d bytes\n", a.size());
00122             sasl->writeIncoming(a);
00123         }
00124         else {
00125             if(sock->canReadLine()) {
00126                 QString line = sock->readLine();
00127                 line.truncate(line.length()-1); // chop the newline
00128                 handleLine(line);
00129             }
00130         }
00131     }
00132 
00133     void sasl_clientFirstStep(bool clientInit, const QByteArray &clientInitData)
00134     {
00135         ++mode;
00136         printf("Choosing mech: %s\n", sasl->mechanism().toLatin1().data());
00137         QString line = sasl->mechanism();
00138         if(clientInit) {
00139             line += ' ';
00140             line += arrayToString(clientInitData);
00141         }
00142         sendLine(line);
00143     }
00144 
00145     void sasl_nextStep(const QByteArray &stepData)
00146     {
00147         QString line = "C";
00148         if(!stepData.isEmpty()) {
00149             line += ',';
00150             line += arrayToString(stepData);
00151         }
00152         sendLine(line);
00153     }
00154 
00155     void sasl_needParams(const QCA::SASL::Params &params)
00156     {
00157         if(params.needUsername())
00158             sasl->setUsername(prompt("Username:"));
00159         if(params.canSendAuthzid()) {
00160             QString authzid = prompt("Authorize As (enter to skip):");
00161             if(!authzid.isEmpty())
00162                 sasl->setAuthzid(authzid);
00163         }
00164         if(params.needPassword()) {
00165             QCA::ConsolePrompt prompt;
00166             prompt.getHidden("* Password");
00167             prompt.waitForFinished();
00168             QCA::SecureArray pass = prompt.result();
00169             sasl->setPassword(pass);
00170         }
00171         if(params.canSendRealm()) {
00172             QStringList realms = sasl->realmList();
00173             printf("Available realms:\n");
00174             foreach(const QString &s, realms)
00175                 printf("  %s\n", qPrintable(s));
00176             sasl->setRealm(prompt("Realm:"));
00177         }
00178         sasl->continueAfterParams();
00179     }
00180 
00181     void sasl_authenticated()
00182     {
00183         printf("SASL success!\n");
00184         printf("SSF: %d\n", sasl->ssf());
00185     }
00186 
00187     void sasl_readyRead()
00188     {
00189         QByteArray a = sasl->read();
00190         int oldsize = inbuf.size();
00191         inbuf.resize(oldsize + a.size());
00192         memcpy(inbuf.data() + oldsize, a.data(), a.size());
00193         processInbuf();
00194     }
00195 
00196     void sasl_readyReadOutgoing()
00197     {
00198         QByteArray a = sasl->readOutgoing();
00199         sock->write(a.data(), a.size());
00200     }
00201 
00202     void sasl_error()
00203     {
00204         printf("SASL error! Auth Condition = %d.\n", sasl->authCondition());
00205         quit();
00206         return;
00207     }
00208 
00209 private:
00210     QTcpSocket *sock;
00211     QCA::SASL *sasl;
00212     int mode;
00213     QString host;
00214     QByteArray inbuf;
00215 
00216     QString arrayToString(const QByteArray &ba)
00217     {
00218         QCA::Base64 encoder;
00219         return encoder.arrayToString(ba);
00220     }
00221 
00222     QByteArray stringToArray(const QString &s)
00223     {
00224         QCA::Base64 decoder(QCA::Decode);
00225         return decoder.stringToArray(s).toByteArray();
00226     }
00227 
00228     void sendLine(const QString &line)
00229     {
00230         printf("Writing: {%s}\n", line.toUtf8().data());
00231         QString s = line + '\n';
00232         QByteArray a = s.toUtf8();
00233         if(mode == 2)
00234             sasl->write(a);
00235         else
00236             sock->write(a.data(), a.length());
00237     }
00238 
00239     void printSocketError(QAbstractSocket::SocketError x)
00240     {
00241         QString s;
00242         if(x == QAbstractSocket::ConnectionRefusedError)
00243             s = "connection refused or timed out";
00244         else if(x == QAbstractSocket::RemoteHostClosedError)
00245             s = "remote host closed the connection";
00246         else if(x == QAbstractSocket::HostNotFoundError)
00247             s = "host not found";
00248         else if(x == QAbstractSocket::SocketAccessError)
00249             s = "access error";
00250         else if(x == QAbstractSocket::SocketResourceError)
00251             s = "too many sockets";
00252         else if(x == QAbstractSocket::SocketTimeoutError)
00253             s = "operation timed out";
00254         else if(x == QAbstractSocket::DatagramTooLargeError)
00255             s = "datagram was larger than system limit";
00256         else if(x == QAbstractSocket::NetworkError)
00257             s = "network error";
00258         else if(x == QAbstractSocket::AddressInUseError)
00259             s = "address is already in use";
00260         else if(x == QAbstractSocket::SocketAddressNotAvailableError)
00261             s = "address does not belong to the host";
00262         else if(x == QAbstractSocket::UnsupportedSocketOperationError)
00263             s = "operation is not supported by the local operating system";
00264         else
00265             s = "unknown socket error";
00266         printf("Socket error: %s\n", s.toLatin1().data());
00267     }
00268 
00269     void processInbuf()
00270     {
00271         QStringList list;
00272         for(int n = 0; n < (int)inbuf.size(); ++n) {
00273             if(inbuf[n] == '\n') {
00274                 list += QString::fromUtf8(inbuf.data(), n);
00275 
00276                 char *p = inbuf.data();
00277                 ++n;
00278                 int x = inbuf.size() - n;
00279                 memmove(p, p + n, x);
00280                 inbuf.resize(x);
00281 
00282                 // start over, basically
00283                 n = -1;
00284             }
00285         }
00286 
00287         foreach(QString line, list)
00288             handleLine(line);
00289     }
00290 
00291     void handleLine(const QString &line)
00292     {
00293         printf("Reading: [%s]\n", line.toLatin1().data());
00294         if(mode == 0) {
00295             // first line is the method list
00296             QStringList mechlist = line.split(' ');
00297             sasl->startClient(PROTO_NAME, host, mechlist);
00298         }
00299         else if(mode == 1) {
00300             QString type, rest;
00301             int n = line.indexOf(',');
00302             if(n != -1) {
00303                 type = line.mid(0, n);
00304                 rest = line.mid(n+1);
00305             }
00306             else {
00307                 type = line;
00308                 rest = "";
00309             }
00310 
00311             if(type == "C") {
00312                 sasl->putStep(stringToArray(rest));
00313             }
00314             else if(type == "E") {
00315                 printf("Authentication failed.\n");
00316                 quit();
00317                 return;
00318             }
00319             else if(type == "A") {
00320                 printf("Authentication success.\n");
00321                 ++mode;
00322                 sock_readyRead(); // any extra data?
00323                 return;
00324             }
00325             else {
00326                 printf("Bad format from peer, closing.\n");
00327                 quit();
00328                 return;
00329             }
00330         }
00331     }
00332 };
00333 
00334 #include "sasltest.moc"
00335 
00336 void usage()
00337 {
00338     printf("usage: sasltest host [user] [pass]\n");
00339 }
00340 
00341 int main(int argc, char **argv)
00342 {
00343     QCA::Initializer init;
00344     QCoreApplication app(argc, argv);
00345 
00346     QString host, user, pass;
00347     QString str = "Hello, World";
00348     if(argc < 2) {
00349         usage();
00350         return 0;
00351     }
00352     host = argv[1];
00353     if(argc >= 3)
00354         user = argv[2];
00355     if(argc >= 4)
00356         pass = argv[3];
00357 
00358     if(!QCA::isSupported("sasl")) {
00359         printf("SASL not supported!\n");
00360         return 1;
00361     }
00362 
00363     ClientTest *c = new ClientTest;
00364     QObject::connect(c, SIGNAL(quit()), &app, SLOT(quit()));
00365     c->start(host, PROTO_PORT, user, pass);
00366     app.exec();
00367     delete c;
00368 
00369     return 0;
00370 }

Generated on Thu Sep 6 19:13:35 2007 for Qt Cryptographic Architecture by  doxygen 1.5.2