summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2022-08-18 16:15:43 +0200
committerPierre Ossman <ossman@cendio.se>2022-08-18 16:26:33 +0200
commitdf8d005de960e2133ded0610656af591e92fc6b7 (patch)
tree063a081f67677576f830675fd9aabbae61ed1cee
parent795494ade1bab6a14fd45e02dbaba52301df65f1 (diff)
downloadnovnc-df8d005de960e2133ded0610656af591e92fc6b7.tar.gz
VeNCrypt should handle classical types
VeNCrypt is a superset of the original security types, so it should be fine to send any of the classical values here as well.
-rw-r--r--core/rfb.js87
-rw-r--r--tests/test.rfb.js58
2 files changed, 109 insertions, 36 deletions
diff --git a/core/rfb.js b/core/rfb.js
index b10b502..4b3526f 100644
--- a/core/rfb.js
+++ b/core/rfb.js
@@ -1524,48 +1524,66 @@ export default class RFB extends EventTargetMixin {
subtypes.push(this._sock.rQshift32());
}
- if (subtypes.indexOf(securityTypePlain) != -1) {
- // 0x100 = 256
- this._sock.send([0, 0, 1, 0]);
- this._rfbVeNCryptState = 4;
- } else {
- return this._fail("VeNCrypt Plain subtype not offered by server");
+ // Look for a matching security type in the order that the
+ // server prefers
+ this._rfbAuthScheme = -1;
+ for (let type of subtypes) {
+ // Avoid getting in to a loop
+ if (type === securityTypeVeNCrypt) {
+ continue;
+ }
+
+ if (this._isSupportedSecurityType(type)) {
+ this._rfbAuthScheme = type;
+ break;
+ }
}
- }
- // negotiated Plain subtype, server waits for password
- if (this._rfbVeNCryptState == 4) {
- if (this._rfbCredentials.username === undefined ||
- this._rfbCredentials.password === undefined) {
- this.dispatchEvent(new CustomEvent(
- "credentialsrequired",
- { detail: { types: ["username", "password"] } }));
- return false;
+ if (this._rfbAuthScheme === -1) {
+ return this._fail("Unsupported security types (types: " + subtypes + ")");
}
- const user = encodeUTF8(this._rfbCredentials.username);
- const pass = encodeUTF8(this._rfbCredentials.password);
-
- this._sock.send([
- (user.length >> 24) & 0xFF,
- (user.length >> 16) & 0xFF,
- (user.length >> 8) & 0xFF,
- user.length & 0xFF
- ]);
- this._sock.send([
- (pass.length >> 24) & 0xFF,
- (pass.length >> 16) & 0xFF,
- (pass.length >> 8) & 0xFF,
- pass.length & 0xFF
- ]);
- this._sock.sendString(user);
- this._sock.sendString(pass);
+ this._sock.send([this._rfbAuthScheme >> 24,
+ this._rfbAuthScheme >> 16,
+ this._rfbAuthScheme >> 8,
+ this._rfbAuthScheme]);
- this._rfbInitState = "SecurityResult";
+ this._rfbVeNCryptState == 4;
return true;
}
}
+ _negotiatePlainAuth() {
+ if (this._rfbCredentials.username === undefined ||
+ this._rfbCredentials.password === undefined) {
+ this.dispatchEvent(new CustomEvent(
+ "credentialsrequired",
+ { detail: { types: ["username", "password"] } }));
+ return false;
+ }
+
+ const user = encodeUTF8(this._rfbCredentials.username);
+ const pass = encodeUTF8(this._rfbCredentials.password);
+
+ this._sock.send([
+ (user.length >> 24) & 0xFF,
+ (user.length >> 16) & 0xFF,
+ (user.length >> 8) & 0xFF,
+ user.length & 0xFF
+ ]);
+ this._sock.send([
+ (pass.length >> 24) & 0xFF,
+ (pass.length >> 16) & 0xFF,
+ (pass.length >> 8) & 0xFF,
+ pass.length & 0xFF
+ ]);
+ this._sock.sendString(user);
+ this._sock.sendString(pass);
+
+ this._rfbInitState = "SecurityResult";
+ return true;
+ }
+
_negotiateStdVNCAuth() {
if (this._sock.rQwait("auth challenge", 16)) { return false; }
@@ -1877,6 +1895,9 @@ export default class RFB extends EventTargetMixin {
case securityTypeVeNCrypt:
return this._negotiateVeNCryptAuth();
+ case securityTypePlain:
+ return this._negotiatePlainAuth();
+
case securityTypeUnixLogon:
return this._negotiateTightUnixAuth();
diff --git a/tests/test.rfb.js b/tests/test.rfb.js
index 0e46ff4..75d1e11 100644
--- a/tests/test.rfb.js
+++ b/tests/test.rfb.js
@@ -1463,18 +1463,70 @@ describe('Remote Frame Buffer Protocol Client', function () {
expect(client._fail).to.have.been.calledOnce;
});
- it('should fail if the Plain authentication is not present', function () {
+ it('should fail if there are no supported subtypes', function () {
// VeNCrypt version
client._sock._websocket._receiveData(new Uint8Array([0, 2]));
expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
// Server ACK.
client._sock._websocket._receiveData(new Uint8Array([0]));
- // Subtype list, only list subtype 1.
+ // Subtype list
sinon.spy(client, "_fail");
- client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 0, 1]));
+ client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 9, 0, 0, 1, 4]));
expect(client._fail).to.have.been.calledOnce;
});
+ it('should support standard types', function () {
+ // VeNCrypt version
+ client._sock._websocket._receiveData(new Uint8Array([0, 2]));
+ expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
+ // Server ACK.
+ client._sock._websocket._receiveData(new Uint8Array([0]));
+ // Subtype list
+ client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 2, 0, 0, 1, 4]));
+
+ let expectedResponse = [];
+ push32(expectedResponse, 2); // Chosen subtype.
+
+ expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
+ });
+
+ it('should respect server preference order', function () {
+ // VeNCrypt version
+ client._sock._websocket._receiveData(new Uint8Array([0, 2]));
+ expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
+ // Server ACK.
+ client._sock._websocket._receiveData(new Uint8Array([0]));
+ // Subtype list
+ let subtypes = [ 6 ];
+ push32(subtypes, 79);
+ push32(subtypes, 30);
+ push32(subtypes, 188);
+ push32(subtypes, 256);
+ push32(subtypes, 6);
+ push32(subtypes, 1);
+ client._sock._websocket._receiveData(new Uint8Array(subtypes));
+
+ let expectedResponse = [];
+ push32(expectedResponse, 30); // Chosen subtype.
+
+ expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
+ });
+
+ it('should ignore redundant VeNCrypt subtype', function () {
+ // VeNCrypt version
+ client._sock._websocket._receiveData(new Uint8Array([0, 2]));
+ expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
+ // Server ACK.
+ client._sock._websocket._receiveData(new Uint8Array([0]));
+ // Subtype list
+ client._sock._websocket._receiveData(new Uint8Array([2, 0, 0, 0, 19, 0, 0, 0, 2]));
+
+ let expectedResponse = [];
+ push32(expectedResponse, 2); // Chosen subtype.
+
+ expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
+ });
+
it('should support Plain authentication', function () {
client._rfbCredentials = { username: 'username', password: 'password' };
// VeNCrypt version