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 class ServerTest : public QTcpServer 00039 { 00040 Q_OBJECT 00041 public: 00042 ServerTest(const QString &_str, int _port) : port(_port) 00043 { 00044 sock = 0; 00045 sasl = 0; 00046 00047 connect(this, SIGNAL(newConnection()), SLOT(serv_newConnection())); 00048 realm.clear(); 00049 str = _str; 00050 } 00051 00052 ~ServerTest() 00053 { 00054 delete sock; 00055 delete sasl; 00056 } 00057 00058 void start() 00059 { 00060 if(!listen(QHostAddress::Any, port)) { 00061 printf("Error binding to port %d!\n", port); 00062 QTimer::singleShot(0, this, SIGNAL(quit())); 00063 return; 00064 } 00065 char myhostname[256]; 00066 int r = gethostname(myhostname, sizeof(myhostname)-1); 00067 if(r == -1) { 00068 printf("Error getting hostname!\n"); 00069 QTimer::singleShot(0, this, SIGNAL(quit())); 00070 return; 00071 } 00072 host = myhostname; 00073 printf("Listening on %s:%d ...\n", host.toLatin1().data(), port); 00074 } 00075 00076 private slots: 00077 void serv_newConnection() 00078 { 00079 // Note: only 1 connection supported at a time in this example! 00080 if(sock) { 00081 delete nextPendingConnection(); 00082 printf("Connection ignored, already have one active.\n"); 00083 return; 00084 } 00085 00086 printf("Connection received! Starting SASL handshake...\n"); 00087 00088 sock = nextPendingConnection(); 00089 connect(sock, SIGNAL(disconnected()), SLOT(sock_connectionClosed())); 00090 connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); 00091 connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError))); 00092 connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(sock_bytesWritten(qint64))); 00093 00094 sasl = new QCA::SASL; 00095 connect(sasl, SIGNAL(authCheck(const QString &, const QString &)), SLOT(sasl_authCheck(const QString &, const QString &))); 00096 connect(sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &))); 00097 connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); 00098 connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); 00099 connect(sasl, SIGNAL(readyReadOutgoing()), SLOT(sasl_readyReadOutgoing())); 00100 connect(sasl, SIGNAL(error()), SLOT(sasl_error())); 00101 connect(sasl, SIGNAL(serverStarted()), SLOT(sasl_serverStarted())); 00102 00103 mode = 0; 00104 inbuf.resize(0); 00105 00106 sasl->setConstraints((QCA::SASL::AuthFlags)(QCA::SASL::AllowPlain | QCA::SASL::AllowAnonymous), 0, 256); 00107 00108 sasl->startServer(PROTO_NAME, host, realm); 00109 } 00110 00111 signals: 00112 void quit(); 00113 00114 private slots: 00115 void sasl_serverStarted() 00116 { 00117 sendLine(sasl->mechanismList().join(" ")); 00118 } 00119 00120 void sock_connectionClosed() 00121 { 00122 printf("Connection closed by peer.\n"); 00123 close(); 00124 } 00125 00126 void sock_error(QAbstractSocket::SocketError x) 00127 { 00128 printSocketError(x); 00129 close(); 00130 } 00131 00132 void sock_readyRead() 00133 { 00134 if(sock->canReadLine()) { 00135 QString line = sock->readLine(); 00136 line.truncate(line.length()-1); // chop the newline 00137 handleLine(line); 00138 } 00139 } 00140 00141 void sock_bytesWritten(qint64 x) 00142 { 00143 if(mode == 2) { 00144 toWrite -= x; 00145 if(toWrite <= 0) { 00146 printf("Sent, closing.\n"); 00147 close(); 00148 } 00149 } 00150 } 00151 00152 void sasl_nextStep(const QByteArray &stepData) 00153 { 00154 QString line = "C"; 00155 if(!stepData.isEmpty()) { 00156 line += ','; 00157 line += arrayToString(stepData.data()); 00158 } 00159 sendLine(line); 00160 } 00161 00162 void sasl_authCheck(const QString &user, const QString &authzid) 00163 { 00164 printf("AuthCheck: User: [%s], Authzid: [%s]\n", user.toLatin1().data(), authzid.toLatin1().data()); 00165 sasl->continueAfterAuthCheck(); 00166 } 00167 00168 void sasl_authenticated() 00169 { 00170 sendLine("A"); 00171 printf("Authentication success.\n"); 00172 ++mode; 00173 printf("SSF: %d\n", sasl->ssf()); 00174 sendLine(str); 00175 } 00176 00177 void sasl_readyRead() 00178 { 00179 QByteArray a = sasl->read(); 00180 int oldsize = inbuf.size(); 00181 inbuf.resize(oldsize + a.size()); 00182 memcpy(inbuf.data() + oldsize, a.data(), a.size()); 00183 processInbuf(); 00184 } 00185 00186 void sasl_readyReadOutgoing() 00187 { 00188 QByteArray a = sasl->readOutgoing(); 00189 toWrite = a.size(); 00190 sock->write(a.data(), a.size()); 00191 } 00192 00193 void sasl_error() 00194 { 00195 QCA::SASL::Error x = sasl->errorCode(); 00196 if(x == QCA::SASL::ErrorInit) { 00197 printf("Problem starting up SASL\n"); 00198 quit(); 00199 } 00200 else if(x == QCA::SASL::ErrorHandshake) { 00201 sendLine("E"); 00202 printf("Authentication failed. AuthCondition = %d.\n", sasl->authCondition()); 00203 if ( sasl->authCondition() == QCA::SASL::NoUser ) { 00204 printf( "No user!\n" ); 00205 } 00206 close(); 00207 } 00208 else if(x == QCA::SASL::ErrorCrypt) { 00209 printf("SASL security layer error!\n"); 00210 close(); 00211 } 00212 } 00213 00214 00215 private: 00216 QString host, realm; 00217 int port; 00218 QString str; 00219 QByteArray inbuf; 00220 int toWrite; 00221 QTcpSocket *sock; 00222 QCA::SASL *sasl; 00223 int mode; 00224 00225 00226 void processInbuf() 00227 { 00228 } 00229 00230 void handleLine(const QString &line) 00231 { 00232 printf("Reading: [%s]\n", line.toLatin1().data()); 00233 if(mode == 0) { 00234 int n = line.indexOf(' '); 00235 if(n != -1) { 00236 QString mech = line.mid(0, n); 00237 QString rest = line.mid(n+1).toUtf8(); 00238 sasl->putServerFirstStep(mech, stringToArray(rest)); 00239 } 00240 else 00241 sasl->putServerFirstStep(line); 00242 ++mode; 00243 } 00244 else if(mode == 1) { 00245 QString type, rest; 00246 int n = line.indexOf(','); 00247 if(n != -1) { 00248 type = line.mid(0, n); 00249 rest = line.mid(n+1); 00250 } 00251 else { 00252 type = line; 00253 rest = ""; 00254 } 00255 00256 if(type == "C") { 00257 sasl->putStep(stringToArray(rest)); 00258 } 00259 else { 00260 printf("Bad format from peer, closing.\n"); 00261 close(); 00262 return; 00263 } 00264 } 00265 } 00266 00267 void close() 00268 { 00269 delete sasl; 00270 sock->deleteLater(); 00271 sock = 0; 00272 sasl = 0; 00273 } 00274 QString arrayToString(const QByteArray &ba) 00275 { 00276 QCA::Base64 encoder; 00277 return encoder.arrayToString(ba); 00278 } 00279 00280 QByteArray stringToArray(const QString &s) 00281 { 00282 QCA::Base64 decoder(QCA::Decode); 00283 return decoder.stringToArray(s).toByteArray(); 00284 } 00285 00286 void sendLine(const QString &line) 00287 { 00288 printf("Writing: {%s}\n", line.toUtf8().data()); 00289 QString s = line + '\n'; 00290 QByteArray a = s.toUtf8(); 00291 if(mode == 2) 00292 sasl->write(a); 00293 else 00294 sock->write(a.data(), a.length()); 00295 } 00296 00297 void printSocketError(QAbstractSocket::SocketError x) 00298 { 00299 QString s; 00300 if(x == QAbstractSocket::ConnectionRefusedError) 00301 s = "connection refused or timed out"; 00302 else if(x == QAbstractSocket::RemoteHostClosedError) 00303 s = "remote host closed the connection"; 00304 else if(x == QAbstractSocket::HostNotFoundError) 00305 s = "host not found"; 00306 else if(x == QAbstractSocket::SocketAccessError) 00307 s = "access error"; 00308 else if(x == QAbstractSocket::SocketResourceError) 00309 s = "too many sockets"; 00310 else if(x == QAbstractSocket::SocketTimeoutError) 00311 s = "operation timed out"; 00312 else if(x == QAbstractSocket::DatagramTooLargeError) 00313 s = "datagram was larger than system limit"; 00314 else if(x == QAbstractSocket::NetworkError) 00315 s = "network error"; 00316 else if(x == QAbstractSocket::AddressInUseError) 00317 s = "address is already in use"; 00318 else if(x == QAbstractSocket::SocketAddressNotAvailableError) 00319 s = "address does not belong to the host"; 00320 else if(x == QAbstractSocket::UnsupportedSocketOperationError) 00321 s = "operation is not supported by the local operating system"; 00322 else 00323 s = "unknown socket error"; 00324 00325 printf("Socket error: %s\n", s.toLatin1().data()); 00326 } 00327 }; 00328 00329 #include "saslservtest.moc" 00330 00331 int main(int argc, char **argv) 00332 { 00333 QCA::Initializer init; 00334 QCoreApplication app(argc, argv); 00335 00336 QString host, user, pass; 00337 QString str = "Hello, World"; 00338 00339 if(argc >= 2) 00340 str = argv[2]; 00341 00342 if(!QCA::isSupported("sasl")) { 00343 printf("SASL not supported!\n"); 00344 return 1; 00345 } 00346 00347 QCA::setAppName("saslservtest"); 00348 00349 ServerTest *s = new ServerTest(str, PROTO_PORT); 00350 QObject::connect(s, SIGNAL(quit()), &app, SLOT(quit())); 00351 s->start(); 00352 app.exec(); 00353 delete s; 00354 00355 return 0; 00356 }