#include "krfblogin.h" #include "krfbconnection.h" /* OPIE */ #include using namespace Opie::Core; /* QT */ #include /* STD */ #include extern "C" { #include "vncauth.h" } // The length of the various messages (used to decide how many bytes to // wait for). const int ServerVersionLength = 12; const int ClientVersionLength = 12; const int AuthSchemeLength = 4; const int FailureReasonSizeLength = 4; const int ChallengeLength = 16; const int AuthResultLength = 4; // Authentication results enum AuthResult { AuthOk, AuthFailed, AuthTooMany }; typedef unsigned char CARD8; typedef unsigned short CARD16; typedef unsigned long CARD32; const int endianTest = 1; // Endian stuff #define Swap16IfLE(s) \ (*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s)) #define Swap32IfLE(l) \ (*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \ (((l) & 0x00ff0000) >> 8) | \ (((l) & 0x0000ff00) << 8) | \ (((l) & 0x000000ff) << 24)) : (l)) KRFBLogin::KRFBLogin( KRFBConnection *con ) : QObject( con, "RFB login manager" ) { assert( con ); this->con = con; currentState = AwaitingServerVersion; connect( this, SIGNAL( error(const QString&) ), con, SIGNAL( error(const QString&) ) ); owarn << "Waiting for server version..." << oendl; static QString statusMsg = tr( "Waiting for server version..." ); emit status( statusMsg ); // Kick off the state machine connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerVersion() ) ); con->waitForData( ServerVersionLength ); } KRFBLogin::~KRFBLogin() { } KRFBLogin::State KRFBLogin::state() const { return currentState; } void KRFBLogin::gotServerVersion() { owarn << "Got server version" << oendl; disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotServerVersion() ) ); // Read the server's version message char serverVersion[ ServerVersionLength + 1 ]; con->read( serverVersion, ServerVersionLength ); serverVersion[ ServerVersionLength ] = '\0'; QCString rfbString( serverVersion, ServerVersionLength + 1 ); versionString = rfbString; QRegExp regexp( "RFB [0-9][0-9][0-9]\\.[0-9][0-9][0-9]\n" ); if ( rfbString.find( regexp ) == -1 ) { static QString msg = tr( "Error: Invalid server version, %1" ).arg( rfbString ); owarn << msg << oendl; emit error( msg ); currentState = Error; return; } // Calculate the actual version number serverMajor = (serverVersion[4] - '0') * 100 + (serverVersion[5] - '0') * 10 + (serverVersion[6] - '0'); serverMinor = (serverVersion[8] - '0') * 100 + (serverVersion[9] - '0') * 10 + (serverVersion[10] - '0'); owarn << "Server Version: " << serverMajor << "." << serverMinor << "" << oendl; if ( serverMajor != 3 ) { QString msg = tr( "Error: Unsupported server version, %1" ) .arg( rfbString ); owarn << msg << oendl; emit error( msg ); currentState = Error; return; } if ( serverMinor != 3 ) { owarn << "Minor version mismatch: " << serverMinor << "" << oendl; } // Setup for the next state sendClientVersion(); connect( con, SIGNAL( gotEnoughData() ), SLOT( gotAuthScheme() ) ); con->waitForData( AuthSchemeLength ); } void KRFBLogin::gotAuthScheme() { disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotAuthScheme() ) ); // Got data CARD32 scheme; con->read( &scheme, AuthSchemeLength ); scheme = Swap32IfLE( scheme ); static QString statusMsgOk = tr( "Logged in" ); switch ( scheme ) { case 0: owarn << "Failed" << oendl; // Handle failure connect( con, SIGNAL( gotEnoughData() ), SLOT( gotFailureReasonSize() ) ); con->waitForData( FailureReasonSizeLength ); break; case 1: // Handle no auth emit status( statusMsgOk ); con->gotRFBConnection(); break; case 2: // Handle VNC auth connect( con, SIGNAL( gotEnoughData() ), SLOT( gotChallenge() ) ); con->waitForData( ChallengeLength ); break; default: owarn << "Unknown authentication scheme, 0x" << scheme << "" << oendl; currentState = Error; break; }; } void KRFBLogin::gotChallenge() { disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotChallenge() ) ); QTimer::singleShot( 0, this, SLOT(getPassword()) ); } void KRFBLogin::getPassword() { // Got data CARD8 challenge[ ChallengeLength ]; con->read( challenge, ChallengeLength ); // Last chance to enter a password if ( con->options_->password.isNull() ) { owarn << "krfblogin needs a password" << oendl; emit passwordRequired( con ); } if ( con->options_->password.isNull() ) { QString msg = tr( "Error: This server requires a password, but none " "has been specified.\n" ); emit error( msg ); return; } vncEncryptBytes( (unsigned char *) challenge, QCString(con->options_->password.latin1()).data() ); con->write( challenge, ChallengeLength ); connect( con, SIGNAL( gotEnoughData() ), SLOT( gotAuthResult() ) ); con->waitForData( AuthResultLength ); } void KRFBLogin::gotFailureReasonSize() { disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotFailureReasonSize() ) ); } void KRFBLogin::gotAuthResult() { // Got data disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotAuthResult() ) ); long result; con->read( &result, AuthResultLength ); result = Swap32IfLE( result ); owarn << "Authentication Result is 0x" << result << "" << oendl; static QString failed = tr( "Error: The password you specified was incorrect." ); static QString tooMany = tr( "Error: Too many invalid login attempts have been made\n" "to this account, please try later." ); static QString statusMsgOk = tr( "Logged in" ); static QString statusMsgFailed = tr( "Login Failed" ); static QString statusMsgTooMany = tr( "Too many failures" ); switch( result ) { case AuthOk: emit status( statusMsgOk ); con->gotRFBConnection(); break; case AuthFailed: owarn << "Dammit" << oendl; emit status( statusMsgFailed ); emit error( failed ); break; case AuthTooMany: emit status( statusMsgTooMany ); emit error( tooMany ); break; default: owarn << "Invalid authentication result, " << result << "" << oendl; break; } } void KRFBLogin::sendClientVersion() { owarn << "Sending client version" << oendl; con->write( (void*)"RFB 003.003\n", ClientVersionLength ); }