}
} catch (AuthFailureException& e) {
vlog.error("AuthFailureException: %s", e.str());
- os->writeU32(secResultFailed);
- if (!client.beforeVersion(3,8)) // 3.8 onwards have failure message
- os->writeString(e.str());
- os->flush();
- throw;
+ state_ = RFBSTATE_SECURITY_FAILURE;
+ authFailure(e.str());
}
}
{
}
+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);
// 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
RFBSTATE_PROTOCOL_VERSION,
RFBSTATE_SECURITY_TYPE,
RFBSTATE_SECURITY,
+ RFBSTATE_SECURITY_FAILURE,
RFBSTATE_QUERYING,
RFBSTATE_INITIALISATION,
RFBSTATE_NORMAL,
losslessTimer(this), server(server_),
updateRenderedCursor(false), removeRenderedCursor(false),
continuousUpdates(false), encodeManager(this), idleTimer(this),
- pointerEventTime(0), clientHasCursor(false)
+ pointerEventTime(0), clientHasCursor(false),
+ authFailureTimer(this)
{
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;
};
}