Merge branch 'authprio' of https://github.com/CendioOssman/noVNC
This commit is contained in:
commit
1d148a8478
223
core/rfb.js
223
core/rfb.js
|
@ -54,6 +54,21 @@ const GESTURE_SCRLSENS = 50;
|
|||
const DOUBLE_TAP_TIMEOUT = 1000;
|
||||
const DOUBLE_TAP_THRESHOLD = 50;
|
||||
|
||||
// Security types
|
||||
const securityTypeNone = 1;
|
||||
const securityTypeVNCAuth = 2;
|
||||
const securityTypeRA2ne = 6;
|
||||
const securityTypeTight = 16;
|
||||
const securityTypeVeNCrypt = 19;
|
||||
const securityTypeXVP = 22;
|
||||
const securityTypeARD = 30;
|
||||
|
||||
// Special Tight security types
|
||||
const securityTypeUnixLogon = 129;
|
||||
|
||||
// VeNCrypt security types
|
||||
const securityTypePlain = 256;
|
||||
|
||||
// Extended clipboard pseudo-encoding formats
|
||||
const extendedClipboardFormatText = 1;
|
||||
/*eslint-disable no-unused-vars */
|
||||
|
@ -402,7 +417,7 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
sendCredentials(creds) {
|
||||
this._rfbCredentials = creds;
|
||||
setTimeout(this._initMsg.bind(this), 0);
|
||||
this._resumeAuthentication();
|
||||
}
|
||||
|
||||
sendCtrlAltDel() {
|
||||
|
@ -922,8 +937,15 @@ export default class RFB extends EventTargetMixin {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 'connecting':
|
||||
while (this._rfbConnectionState === 'connecting') {
|
||||
if (!this._initMsg()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this._initMsg();
|
||||
Log.Error("Got data while in an invalid state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1332,6 +1354,21 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbInitState = 'Security';
|
||||
}
|
||||
|
||||
_isSupportedSecurityType(type) {
|
||||
const clientTypes = [
|
||||
securityTypeNone,
|
||||
securityTypeVNCAuth,
|
||||
securityTypeRA2ne,
|
||||
securityTypeTight,
|
||||
securityTypeVeNCrypt,
|
||||
securityTypeXVP,
|
||||
securityTypeARD,
|
||||
securityTypePlain,
|
||||
];
|
||||
|
||||
return clientTypes.includes(type);
|
||||
}
|
||||
|
||||
_negotiateSecurity() {
|
||||
if (this._rfbVersion >= 3.7) {
|
||||
// Server sends supported list, client decides
|
||||
|
@ -1342,28 +1379,23 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbInitState = "SecurityReason";
|
||||
this._securityContext = "no security types";
|
||||
this._securityStatus = 1;
|
||||
return this._initMsg();
|
||||
return true;
|
||||
}
|
||||
|
||||
const types = this._sock.rQshiftBytes(numTypes);
|
||||
Log.Debug("Server security types: " + types);
|
||||
|
||||
// Look for each auth in preferred order
|
||||
if (types.includes(1)) {
|
||||
this._rfbAuthScheme = 1; // None
|
||||
} else if (types.includes(22)) {
|
||||
this._rfbAuthScheme = 22; // XVP
|
||||
} else if (types.includes(16)) {
|
||||
this._rfbAuthScheme = 16; // Tight
|
||||
} else if (types.includes(6)) {
|
||||
this._rfbAuthScheme = 6; // RA2ne Auth
|
||||
} else if (types.includes(2)) {
|
||||
this._rfbAuthScheme = 2; // VNC Auth
|
||||
} else if (types.includes(30)) {
|
||||
this._rfbAuthScheme = 30; // ARD Auth
|
||||
} else if (types.includes(19)) {
|
||||
this._rfbAuthScheme = 19; // VeNCrypt Auth
|
||||
} else {
|
||||
// Look for a matching security type in the order that the
|
||||
// server prefers
|
||||
this._rfbAuthScheme = -1;
|
||||
for (let type of types) {
|
||||
if (this._isSupportedSecurityType(type)) {
|
||||
this._rfbAuthScheme = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._rfbAuthScheme === -1) {
|
||||
return this._fail("Unsupported security types (types: " + types + ")");
|
||||
}
|
||||
|
||||
|
@ -1377,14 +1409,14 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbInitState = "SecurityReason";
|
||||
this._securityContext = "authentication scheme";
|
||||
this._securityStatus = 1;
|
||||
return this._initMsg();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
this._rfbInitState = 'Authentication';
|
||||
Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);
|
||||
|
||||
return this._initMsg(); // jump to authentication
|
||||
return true;
|
||||
}
|
||||
|
||||
_handleSecurityReason() {
|
||||
|
@ -1434,7 +1466,7 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbCredentials.username +
|
||||
this._rfbCredentials.target;
|
||||
this._sock.sendString(xvpAuthStr);
|
||||
this._rfbAuthScheme = 2;
|
||||
this._rfbAuthScheme = securityTypeVNCAuth;
|
||||
return this._negotiateAuthentication();
|
||||
}
|
||||
|
||||
|
@ -1492,49 +1524,66 @@ export default class RFB extends EventTargetMixin {
|
|||
subtypes.push(this._sock.rQshift32());
|
||||
}
|
||||
|
||||
// 256 = Plain subtype
|
||||
if (subtypes.indexOf(256) != -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;
|
||||
}
|
||||
|
||||
// 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._isSupportedSecurityType(type)) {
|
||||
this._rfbAuthScheme = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const user = encodeUTF8(this._rfbCredentials.username);
|
||||
const pass = encodeUTF8(this._rfbCredentials.password);
|
||||
if (this._rfbAuthScheme === -1) {
|
||||
return this._fail("Unsupported security types (types: " + subtypes + ")");
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
|
@ -1661,7 +1710,7 @@ export default class RFB extends EventTargetMixin {
|
|||
this._rfbCredentials.ardCredentials = encrypted;
|
||||
this._rfbCredentials.ardPublicKey = clientPublicKey;
|
||||
|
||||
setTimeout(this._initMsg.bind(this), 0);
|
||||
this._resumeAuthentication();
|
||||
}
|
||||
|
||||
_negotiateTightUnixAuth() {
|
||||
|
@ -1771,12 +1820,12 @@ export default class RFB extends EventTargetMixin {
|
|||
case 'STDVNOAUTH__': // no auth
|
||||
this._rfbInitState = 'SecurityResult';
|
||||
return true;
|
||||
case 'STDVVNCAUTH_': // VNC auth
|
||||
this._rfbAuthScheme = 2;
|
||||
return this._initMsg();
|
||||
case 'TGHTULGNAUTH': // UNIX auth
|
||||
this._rfbAuthScheme = 129;
|
||||
return this._initMsg();
|
||||
case 'STDVVNCAUTH_':
|
||||
this._rfbAuthScheme = securityTypeVNCAuth;
|
||||
return true;
|
||||
case 'TGHTULGNAUTH':
|
||||
this._rfbAuthScheme = securityTypeUnixLogon;
|
||||
return true;
|
||||
default:
|
||||
return this._fail("Unsupported tiny auth scheme " +
|
||||
"(scheme: " + authType + ")");
|
||||
|
@ -1813,7 +1862,7 @@ export default class RFB extends EventTargetMixin {
|
|||
}).then(() => {
|
||||
this.dispatchEvent(new CustomEvent('securityresult'));
|
||||
this._rfbInitState = "SecurityResult";
|
||||
this._initMsg();
|
||||
return true;
|
||||
}).finally(() => {
|
||||
this._rfbRSAAESAuthenticationState.removeEventListener(
|
||||
"serververification", this._eventHandlers.handleRSAAESServerVerification);
|
||||
|
@ -1827,33 +1876,32 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
_negotiateAuthentication() {
|
||||
switch (this._rfbAuthScheme) {
|
||||
case 1: // no auth
|
||||
if (this._rfbVersion >= 3.8) {
|
||||
this._rfbInitState = 'SecurityResult';
|
||||
return true;
|
||||
}
|
||||
this._rfbInitState = 'ClientInitialisation';
|
||||
return this._initMsg();
|
||||
case securityTypeNone:
|
||||
this._rfbInitState = 'SecurityResult';
|
||||
return true;
|
||||
|
||||
case 22: // XVP auth
|
||||
case securityTypeXVP:
|
||||
return this._negotiateXvpAuth();
|
||||
|
||||
case 30: // ARD auth
|
||||
case securityTypeARD:
|
||||
return this._negotiateARDAuth();
|
||||
|
||||
case 2: // VNC authentication
|
||||
case securityTypeVNCAuth:
|
||||
return this._negotiateStdVNCAuth();
|
||||
|
||||
case 16: // TightVNC Security Type
|
||||
case securityTypeTight:
|
||||
return this._negotiateTightAuth();
|
||||
|
||||
case 19: // VeNCrypt Security Type
|
||||
case securityTypeVeNCrypt:
|
||||
return this._negotiateVeNCryptAuth();
|
||||
|
||||
case 129: // TightVNC UNIX Security Type
|
||||
case securityTypePlain:
|
||||
return this._negotiatePlainAuth();
|
||||
|
||||
case securityTypeUnixLogon:
|
||||
return this._negotiateTightUnixAuth();
|
||||
|
||||
case 6: // RA2ne Security Type
|
||||
case securityTypeRA2ne:
|
||||
return this._negotiateRA2neAuth();
|
||||
|
||||
default:
|
||||
|
@ -1863,6 +1911,13 @@ export default class RFB extends EventTargetMixin {
|
|||
}
|
||||
|
||||
_handleSecurityResult() {
|
||||
// There is no security choice, and hence no security result
|
||||
// until RFB 3.7
|
||||
if (this._rfbVersion < 3.7) {
|
||||
this._rfbInitState = 'ClientInitialisation';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
|
||||
|
||||
const status = this._sock.rQshift32();
|
||||
|
@ -1870,13 +1925,13 @@ export default class RFB extends EventTargetMixin {
|
|||
if (status === 0) { // OK
|
||||
this._rfbInitState = 'ClientInitialisation';
|
||||
Log.Debug('Authentication OK');
|
||||
return this._initMsg();
|
||||
return true;
|
||||
} else {
|
||||
if (this._rfbVersion >= 3.8) {
|
||||
this._rfbInitState = "SecurityReason";
|
||||
this._securityContext = "security result";
|
||||
this._securityStatus = status;
|
||||
return this._initMsg();
|
||||
return true;
|
||||
} else {
|
||||
this.dispatchEvent(new CustomEvent(
|
||||
"securityfailure",
|
||||
|
@ -2052,6 +2107,14 @@ export default class RFB extends EventTargetMixin {
|
|||
}
|
||||
}
|
||||
|
||||
// Resume authentication handshake after it was paused for some
|
||||
// reason, e.g. waiting for a password from the user
|
||||
_resumeAuthentication() {
|
||||
// We use setTimeout() so it's run in its own context, just like
|
||||
// it originally did via the WebSocket's event handler
|
||||
setTimeout(this._initMsg.bind(this), 0);
|
||||
}
|
||||
|
||||
_handleSetColourMapMsg() {
|
||||
Log.Debug("SetColorMapEntries");
|
||||
|
||||
|
|
|
@ -1026,17 +1026,21 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
client._rfbConnectionState = 'connecting';
|
||||
});
|
||||
|
||||
describe('ProtocolVersion', function () {
|
||||
function sendVer(ver, client) {
|
||||
const arr = new Uint8Array(12);
|
||||
for (let i = 0; i < ver.length; i++) {
|
||||
arr[i+4] = ver.charCodeAt(i);
|
||||
}
|
||||
arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
|
||||
arr[11] = '\n';
|
||||
client._sock._websocket._receiveData(arr);
|
||||
function sendVer(ver, client) {
|
||||
const arr = new Uint8Array(12);
|
||||
for (let i = 0; i < ver.length; i++) {
|
||||
arr[i+4] = ver.charCodeAt(i);
|
||||
}
|
||||
arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
|
||||
arr[11] = '\n';
|
||||
client._sock._websocket._receiveData(arr);
|
||||
}
|
||||
|
||||
function sendSecurity(type, cl) {
|
||||
cl._sock._websocket._receiveData(new Uint8Array([1, type]));
|
||||
}
|
||||
|
||||
describe('ProtocolVersion', function () {
|
||||
describe('version parsing', function () {
|
||||
it('should interpret version 003.003 as version 3.3', function () {
|
||||
sendVer('003.003', client);
|
||||
|
@ -1127,44 +1131,24 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
|
||||
describe('Security', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
sendVer('003.008\n', client);
|
||||
client._sock._websocket._getSentData();
|
||||
});
|
||||
|
||||
it('should simply receive the auth scheme when for versions < 3.7', function () {
|
||||
client._rfbVersion = 3.6;
|
||||
const authSchemeRaw = [1, 2, 3, 4];
|
||||
const authScheme = (authSchemeRaw[0] << 24) + (authSchemeRaw[1] << 16) +
|
||||
(authSchemeRaw[2] << 8) + authSchemeRaw[3];
|
||||
client._sock._websocket._receiveData(new Uint8Array(authSchemeRaw));
|
||||
expect(client._rfbAuthScheme).to.equal(authScheme);
|
||||
});
|
||||
|
||||
it('should prefer no authentication is possible', function () {
|
||||
client._rfbVersion = 3.7;
|
||||
const authSchemes = [2, 1, 3];
|
||||
it('should respect server preference order', function () {
|
||||
const authSchemes = [ 6, 79, 30, 188, 16, 6, 1 ];
|
||||
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
|
||||
expect(client._rfbAuthScheme).to.equal(1);
|
||||
expect(client._sock).to.have.sent(new Uint8Array([1, 1]));
|
||||
expect(client._sock).to.have.sent(new Uint8Array([30]));
|
||||
});
|
||||
|
||||
it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
|
||||
client._rfbVersion = 3.7;
|
||||
const authSchemes = [2, 22, 16];
|
||||
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
|
||||
expect(client._rfbAuthScheme).to.equal(22);
|
||||
expect(client._sock).to.have.sent(new Uint8Array([22]));
|
||||
});
|
||||
|
||||
it('should fail if there are no supported schemes for versions >= 3.7', function () {
|
||||
it('should fail if there are no supported schemes', function () {
|
||||
sinon.spy(client, "_fail");
|
||||
client._rfbVersion = 3.7;
|
||||
const authSchemes = [1, 32];
|
||||
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
|
||||
expect(client._fail).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
|
||||
client._rfbVersion = 3.7;
|
||||
it('should fail with the appropriate message if no types are sent', function () {
|
||||
const failureData = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
|
||||
sinon.spy(client, '_fail');
|
||||
client._sock._websocket._receiveData(new Uint8Array(failureData));
|
||||
|
@ -1175,7 +1159,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
});
|
||||
|
||||
it('should transition to the Authentication state and continue on successful negotiation', function () {
|
||||
client._rfbVersion = 3.7;
|
||||
const authSchemes = [1, 1];
|
||||
client._negotiateAuthentication = sinon.spy();
|
||||
client._sock._websocket._receiveData(new Uint8Array(authSchemes));
|
||||
|
@ -1184,17 +1167,8 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Authentication', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
});
|
||||
|
||||
function sendSecurity(type, cl) {
|
||||
cl._sock._websocket._receiveData(new Uint8Array([1, type]));
|
||||
}
|
||||
|
||||
describe('Legacy Authentication', function () {
|
||||
it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
|
||||
client._rfbVersion = 3.6;
|
||||
const errMsg = "Whoopsies";
|
||||
const data = [0, 0, 0, 0];
|
||||
const errLen = errMsg.length;
|
||||
|
@ -1203,37 +1177,42 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
data.push(errMsg.charCodeAt(i));
|
||||
}
|
||||
|
||||
sendVer('003.006\n', client);
|
||||
client._sock._websocket._getSentData();
|
||||
|
||||
sinon.spy(client, '_fail');
|
||||
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||
expect(client._fail).to.have.been.calledWith(
|
||||
'Security negotiation failed on authentication scheme (reason: Whoopsies)');
|
||||
});
|
||||
|
||||
it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
|
||||
client._rfbVersion = 3.8;
|
||||
it('should transition straight to ServerInitialisation on "no auth" for versions < 3.7', function () {
|
||||
sendVer('003.006\n', client);
|
||||
client._sock._websocket._getSentData();
|
||||
|
||||
client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1]));
|
||||
expect(client._rfbInitState).to.equal('ServerInitialisation');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Authentication', function () {
|
||||
beforeEach(function () {
|
||||
sendVer('003.008\n', client);
|
||||
client._sock._websocket._getSentData();
|
||||
});
|
||||
|
||||
it('should transition straight to SecurityResult on "no auth" (1)', function () {
|
||||
sendSecurity(1, client);
|
||||
expect(client._rfbInitState).to.equal('SecurityResult');
|
||||
});
|
||||
|
||||
it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
|
||||
client._rfbVersion = 3.7;
|
||||
sendSecurity(1, client);
|
||||
expect(client._rfbInitState).to.equal('ServerInitialisation');
|
||||
});
|
||||
|
||||
it('should fail on an unknown auth scheme', function () {
|
||||
sinon.spy(client, "_fail");
|
||||
client._rfbVersion = 3.8;
|
||||
sendSecurity(57, client);
|
||||
expect(client._fail).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
describe('VNC Authentication (type 2) Handler', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
client._rfbVersion = 3.8;
|
||||
});
|
||||
|
||||
it('should fire the credentialsrequired event if missing a password', function () {
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("credentialsrequired", spy);
|
||||
|
@ -1274,12 +1253,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
});
|
||||
|
||||
describe('ARD Authentication (type 30) Handler', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
client._rfbVersion = 3.8;
|
||||
});
|
||||
|
||||
it('should fire the credentialsrequired event if all credentials are missing', function () {
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("credentialsrequired", spy);
|
||||
|
@ -1347,11 +1320,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
});
|
||||
|
||||
describe('XVP Authentication (type 22) Handler', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
client._rfbVersion = 3.8;
|
||||
});
|
||||
|
||||
it('should fall through to standard VNC authentication upon completion', function () {
|
||||
client._rfbCredentials = { username: 'user',
|
||||
target: 'target',
|
||||
|
@ -1400,8 +1368,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
|
||||
describe('TightVNC Authentication (type 16) Handler', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
client._rfbVersion = 3.8;
|
||||
sendSecurity(16, client);
|
||||
client._sock._websocket._getSentData(); // skip the security reply
|
||||
});
|
||||
|
@ -1487,8 +1453,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
|
||||
describe('VeNCrypt Authentication (type 19) Handler', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'Security';
|
||||
client._rfbVersion = 3.8;
|
||||
sendSecurity(19, client);
|
||||
expect(client._sock).to.have.sent(new Uint8Array([19]));
|
||||
});
|
||||
|
@ -1499,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
|
||||
|
@ -1582,9 +1598,30 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Legacy SecurityResult', function () {
|
||||
beforeEach(function () {
|
||||
sendVer('003.007\n', client);
|
||||
client._sock._websocket._getSentData();
|
||||
sendSecurity(1, client);
|
||||
client._sock._websocket._getSentData();
|
||||
});
|
||||
|
||||
it('should not include reason in securityfailure event', function () {
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("securityfailure", spy);
|
||||
client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
|
||||
expect(spy).to.have.been.calledOnce;
|
||||
expect(spy.args[0][0].detail.status).to.equal(2);
|
||||
expect('reason' in spy.args[0][0].detail).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('SecurityResult', function () {
|
||||
beforeEach(function () {
|
||||
client._rfbInitState = 'SecurityResult';
|
||||
sendVer('003.008\n', client);
|
||||
client._sock._websocket._getSentData();
|
||||
sendSecurity(1, client);
|
||||
client._sock._websocket._getSentData();
|
||||
});
|
||||
|
||||
it('should fall through to ServerInitialisation on a response code of 0', function () {
|
||||
|
@ -1592,60 +1629,26 @@ describe('Remote Frame Buffer Protocol Client', function () {
|
|||
expect(client._rfbInitState).to.equal('ServerInitialisation');
|
||||
});
|
||||
|
||||
it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
|
||||
client._rfbVersion = 3.8;
|
||||
sinon.spy(client, '_fail');
|
||||
const failureData = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
|
||||
client._sock._websocket._receiveData(new Uint8Array(failureData));
|
||||
expect(client._fail).to.have.been.calledWith(
|
||||
'Security negotiation failed on security result (reason: whoops)');
|
||||
});
|
||||
|
||||
it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
|
||||
sinon.spy(client, '_fail');
|
||||
client._rfbVersion = 3.7;
|
||||
client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1]));
|
||||
expect(client._fail).to.have.been.calledWith(
|
||||
'Security handshake failed');
|
||||
});
|
||||
|
||||
it('should result in securityfailure event when receiving a non zero status', function () {
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("securityfailure", spy);
|
||||
client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
|
||||
expect(spy).to.have.been.calledOnce;
|
||||
expect(spy.args[0][0].detail.status).to.equal(2);
|
||||
});
|
||||
|
||||
it('should include reason when provided in securityfailure event', function () {
|
||||
client._rfbVersion = 3.8;
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("securityfailure", spy);
|
||||
const failureData = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
|
||||
32, 102, 97, 105, 108, 117, 114, 101];
|
||||
client._sock._websocket._receiveData(new Uint8Array(failureData));
|
||||
expect(spy).to.have.been.calledOnce;
|
||||
expect(spy.args[0][0].detail.status).to.equal(1);
|
||||
expect(spy.args[0][0].detail.reason).to.equal('such failure');
|
||||
});
|
||||
|
||||
it('should not include reason when length is zero in securityfailure event', function () {
|
||||
client._rfbVersion = 3.9;
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("securityfailure", spy);
|
||||
const failureData = [0, 0, 0, 1, 0, 0, 0, 0];
|
||||
client._sock._websocket._receiveData(new Uint8Array(failureData));
|
||||
expect(spy).to.have.been.calledOnce;
|
||||
expect(spy.args[0][0].detail.status).to.equal(1);
|
||||
expect('reason' in spy.args[0][0].detail).to.be.false;
|
||||
});
|
||||
|
||||
it('should not include reason in securityfailure event for version < 3.8', function () {
|
||||
client._rfbVersion = 3.6;
|
||||
const spy = sinon.spy();
|
||||
client.addEventListener("securityfailure", spy);
|
||||
client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
|
||||
expect(spy.args[0][0].detail.status).to.equal(2);
|
||||
expect('reason' in spy.args[0][0].detail).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('ClientInitialisation', function () {
|
||||
|
|
Loading…
Reference in New Issue