SConnection::SConnection()
: readyForSetColourMapEntries(false),
- is(0), os(0), reader_(0), writer_(0),
- ssecurity(0), state_(RFBSTATE_UNINITIALISED),
- preferredEncoding(encodingRaw),
+ is(0), os(0), reader_(0), writer_(0), ssecurity(0),
+ authFailureTimer(this, &SConnection::handleAuthFailureTimeout),
+ state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw),
clientClipboard(NULL), hasLocalClipboard(false)
{
defaultMajorVersion = 3;
case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
case RFBSTATE_SECURITY: processSecurityMsg(); break;
+ case RFBSTATE_SECURITY_FAILURE: processSecurityFailure(); break;
case RFBSTATE_INITIALISATION: processInitMsg(); break;
case RFBSTATE_NORMAL: reader_->readMsg(); break;
case RFBSTATE_QUERYING:
} catch (AuthFailureException& e) {
vlog.error("AuthFailureException: %s", e.str());
state_ = RFBSTATE_SECURITY_FAILURE;
- authFailure(e.str());
+ // Introduce a slight delay of the authentication failure response
+ // to make it difficult to brute force a password
+ authFailureMsg.replaceBuf(strDup(e.str()));
+ authFailureTimer.start(100);
}
}
+void SConnection::processSecurityFailure()
+{
+ // Silently drop any data if we are currently delaying an
+ // authentication failure response as otherwise we would close
+ // the connection on unexpected data, and an attacker could use
+ // that to detect our delayed state.
+
+ while (is->checkNoWait(1))
+ is->skip(1);
+}
+
void SConnection::processInitMsg()
{
vlog.debug("reading client initialisation");
reader_->readClientInit();
}
+bool SConnection::handleAuthFailureTimeout(Timer* t)
+{
+ if (state_ != RFBSTATE_SECURITY_FAILURE) {
+ close("SConnection::handleAuthFailureTimeout: invalid state");
+ return false;
+ }
+
+ try {
+ os->writeU32(secResultFailed);
+ if (!client.beforeVersion(3,8)) // 3.8 onwards have failure message
+ os->writeString(authFailureMsg.buf);
+ os->flush();
+ } catch (rdr::Exception& e) {
+ close(e.str());
+ return false;
+ }
+
+ close(authFailureMsg.buf);
+
+ return false;
+}
+
void SConnection::throwConnFailedException(const char* format, ...)
{
va_list ap;
{
}
-void SConnection::authFailure(const char* reason)
-{
- if (state_ != RFBSTATE_SECURITY_FAILURE)
- throw Exception("SConnection::authFailure: invalid state");
-
- os->writeU32(secResultFailed);
- if (!client.beforeVersion(3,8)) // 3.8 onwards have failure message
- os->writeString(reason);
- os->flush();
-
- throw AuthFailureException(reason);
-}
-
void SConnection::queryConnection(const char* userName)
{
approveConnection(true);
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
+
#include <rfb/SMsgHandler.h>
#include <rfb/SecurityServer.h>
+#include <rfb/Timer.h>
namespace rfb {
// authSuccess() is called when authentication has succeeded.
virtual void authSuccess();
- // authFailure() is called when authentication has failed. The default
- // implementation will inform the client and throw a AuthFailureException.
- virtual void authFailure(const char* reason);
-
// queryConnection() is called when authentication has succeeded, but
// before informing the client. It can be overridden to query a local user
// to accept the incoming connection, for example. The userName argument
void processSecurityTypeMsg();
void processSecurityType(int secType);
void processSecurityMsg();
+ void processSecurityFailure();
void processInitMsg();
+ bool handleAuthFailureTimeout(Timer* t);
+
int defaultMajorVersion, defaultMinorVersion;
+
rdr::InStream* is;
rdr::OutStream* os;
+
SMsgReader* reader_;
SMsgWriter* writer_;
+
SecurityServer security;
SSecurity* ssecurity;
+
+ MethodTimer<SConnection> authFailureTimer;
+ CharArray authFailureMsg;
+
stateEnum state_;
rdr::S32 preferredEncoding;
AccessRights accessRights;
losslessTimer(this), server(server_),
updateRenderedCursor(false), removeRenderedCursor(false),
continuousUpdates(false), encodeManager(this), idleTimer(this),
- pointerEventTime(0), clientHasCursor(false),
- authFailureTimer(this)
+ pointerEventTime(0), clientHasCursor(false)
{
setStreams(&sock->inStream(), &sock->outStream());
peerEndpoint.buf = sock->getPeerEndpoint();
sock->cork(true);
while (getInStream()->checkNoWait(1)) {
- // Silently drop any data if we are currently delaying an
- // authentication failure response as otherwise we would close
- // the connection on unexpected data, and an attacker could use
- // that to detect our delayed state.
- if (state() == RFBSTATE_SECURITY_FAILURE) {
- getInStream()->skip(1);
- continue;
- }
-
if (pendingSyncFence) {
syncFence = true;
pendingSyncFence = false;
updates.add_changed(server->getPixelBuffer()->getRect());
}
-void VNCSConnectionST::authFailure(const char* reason)
-{
- // Introduce a slight delay of the authentication failure response
- // to make it difficult to brute force a password
- authFailureMsg.replaceBuf(strDup(reason));
- authFailureTimer.start(100);
-}
-
void VNCSConnectionST::queryConnection(const char* userName)
{
server->queryConnection(this, userName);
if ((t == &congestionTimer) ||
(t == &losslessTimer))
writeFramebufferUpdate();
- else if (t == &authFailureTimer)
- SConnection::authFailure(authFailureMsg.buf);
} catch (rdr::Exception& e) {
close(e.str());
}
// These methods are invoked as callbacks from processMsg()
virtual void authSuccess();
- virtual void authFailure(const char* reason);
virtual void queryConnection(const char* userName);
virtual void clientInit(bool shared);
virtual void setPixelFormat(const PixelFormat& pf);
Point pointerEventPos;
bool clientHasCursor;
- Timer authFailureTimer;
- CharArray authFailureMsg;
-
CharArray closeReason;
};
}