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 ¶ms) 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 }