Allow other credentials than just password

Makes the XVP authentication mechanism more general.
This commit is contained in:
Pierre Ossman 2017-10-13 13:50:49 +02:00
parent 233c8b6a53
commit 430f00d6fe
6 changed files with 105 additions and 98 deletions

View File

@ -206,7 +206,7 @@ var UI = {
'onNotification': UI.notification, 'onNotification': UI.notification,
'onUpdateState': UI.updateState, 'onUpdateState': UI.updateState,
'onDisconnected': UI.disconnectFinished, 'onDisconnected': UI.disconnectFinished,
'onPasswordRequired': UI.passwordRequired, 'onCredentialsRequired': UI.credentials,
'onXvpInit': UI.updateXvpButton, 'onXvpInit': UI.updateXvpButton,
'onClipboard': UI.clipboardReceive, 'onClipboard': UI.clipboardReceive,
'onBell': UI.bell, 'onBell': UI.bell,
@ -1067,7 +1067,7 @@ var UI = {
UI.updateLocalCursor(); UI.updateLocalCursor();
UI.updateViewOnly(); UI.updateViewOnly();
UI.rfb.connect(host, port, password, path); UI.rfb.connect(host, port, { password: password }, path);
}, },
disconnect: function() { disconnect: function() {
@ -1127,8 +1127,8 @@ var UI = {
* PASSWORD * PASSWORD
* ------v------*/ * ------v------*/
passwordRequired: function(rfb, msg) { credentials: function(rfb, types) {
// FIXME: handle more types
document.getElementById('noVNC_password_dlg') document.getElementById('noVNC_password_dlg')
.classList.add('noVNC_open'); .classList.add('noVNC_open');
@ -1136,9 +1136,7 @@ var UI = {
document.getElementById('noVNC_password_input').focus(); document.getElementById('noVNC_password_input').focus();
}, 100); }, 100);
if (typeof msg === 'undefined') { var msg = _("Password is required");
msg = _("Password is required");
}
Log.Warn(msg); Log.Warn(msg);
UI.showStatus(msg, "warning"); UI.showStatus(msg, "warning");
}, },
@ -1148,7 +1146,7 @@ var UI = {
var password = inputElem.value; var password = inputElem.value;
// Clear the input after reading the password // Clear the input after reading the password
inputElem.value = ""; inputElem.value = "";
UI.rfb.sendPassword(password); UI.rfb.sendCredentials({ password: password });
UI.reconnect_password = password; UI.reconnect_password = password;
document.getElementById('noVNC_password_dlg') document.getElementById('noVNC_password_dlg')
.classList.remove('noVNC_open'); .classList.remove('noVNC_open');

View File

@ -36,7 +36,7 @@ export default function RFB(defaults) {
this._rfb_host = ''; this._rfb_host = '';
this._rfb_port = 5900; this._rfb_port = 5900;
this._rfb_password = ''; this._rfb_credentials = {};
this._rfb_path = ''; this._rfb_path = '';
this._rfb_connection_state = ''; this._rfb_connection_state = '';
@ -124,7 +124,6 @@ export default function RFB(defaults) {
'local_cursor': false, // Request locally rendered cursor 'local_cursor': false, // Request locally rendered cursor
'shared': true, // Request shared mode 'shared': true, // Request shared mode
'view_only': false, // Disable client mouse/keyboard 'view_only': false, // Disable client mouse/keyboard
'xvp_password_sep': '@', // Separator for XVP password fields
'disconnectTimeout': 3, // Time (s) to wait for disconnection 'disconnectTimeout': 3, // Time (s) to wait for disconnection
'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection 'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
'repeaterID': '', // [UltraVNC] RepeaterID to connect to 'repeaterID': '', // [UltraVNC] RepeaterID to connect to
@ -134,7 +133,7 @@ export default function RFB(defaults) {
'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change 'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change
'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI 'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI
'onDisconnected': function () { }, // onDisconnected(rfb, reason): disconnection finished 'onDisconnected': function () { }, // onDisconnected(rfb, reason): disconnection finished
'onPasswordRequired': function () { }, // onPasswordRequired(rfb, msg): VNC password is required 'onCredentialsRequired': function () { }, // onCredentialsRequired(rfb, types): VNC credentials are required
'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received 'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received
'onBell': function () { }, // onBell(rfb): RFB Bell message received 'onBell': function () { }, // onBell(rfb): RFB Bell message received
'onFBUReceive': function () { }, // onFBUReceive(rfb, rect): RFB FBU rect received but not yet processed 'onFBUReceive': function () { }, // onFBUReceive(rfb, rect): RFB FBU rect received but not yet processed
@ -241,10 +240,10 @@ export default function RFB(defaults) {
RFB.prototype = { RFB.prototype = {
// Public methods // Public methods
connect: function (host, port, password, path) { connect: function (host, port, creds, path) {
this._rfb_host = host; this._rfb_host = host;
this._rfb_port = port; this._rfb_port = port;
this._rfb_password = (password !== undefined) ? password : ""; this._rfb_credentials = (creds !== undefined) ? creds : {};
this._rfb_path = (path !== undefined) ? path : ""; this._rfb_path = (path !== undefined) ? path : "";
if (!this._rfb_host) { if (!this._rfb_host) {
@ -264,8 +263,8 @@ RFB.prototype = {
this._sock.off('open'); this._sock.off('open');
}, },
sendPassword: function (passwd) { sendCredentials: function (creds) {
this._rfb_password = passwd; this._rfb_credentials = creds;
setTimeout(this._init_msg.bind(this), 0); setTimeout(this._init_msg.bind(this), 0);
}, },
@ -848,21 +847,18 @@ RFB.prototype = {
// authentication // authentication
_negotiate_xvp_auth: function () { _negotiate_xvp_auth: function () {
var xvp_sep = this._xvp_password_sep; if (!this._rfb_credentials.username ||
var xvp_auth = this._rfb_password.split(xvp_sep); !this._rfb_credentials.password ||
if (xvp_auth.length < 3) { !this._rfb_credentials.target) {
var msg = 'XVP credentials required (user' + xvp_sep + this._onCredentialsRequired(this, ["username", "password", "target"]);
'target' + xvp_sep + 'password) -- got only ' + this._rfb_password;
this._onPasswordRequired(this, msg);
return false; return false;
} }
var xvp_auth_str = String.fromCharCode(xvp_auth[0].length) + var xvp_auth_str = String.fromCharCode(this._rfb_credentials.username.length) +
String.fromCharCode(xvp_auth[1].length) + String.fromCharCode(this._rfb_credentials.target.length) +
xvp_auth[0] + this._rfb_credentials.username +
xvp_auth[1]; this._rfb_credentials.target;
this._sock.send_string(xvp_auth_str); this._sock.send_string(xvp_auth_str);
this._rfb_password = xvp_auth.slice(2).join(xvp_sep);
this._rfb_auth_scheme = 2; this._rfb_auth_scheme = 2;
return this._negotiate_authentication(); return this._negotiate_authentication();
}, },
@ -870,14 +866,14 @@ RFB.prototype = {
_negotiate_std_vnc_auth: function () { _negotiate_std_vnc_auth: function () {
if (this._sock.rQwait("auth challenge", 16)) { return false; } if (this._sock.rQwait("auth challenge", 16)) { return false; }
if (this._rfb_password.length === 0) { if (!this._rfb_credentials.password) {
this._onPasswordRequired(this); this._onCredentialsRequired(this, ["password"]);
return false; return false;
} }
// TODO(directxman12): make genDES not require an Array // TODO(directxman12): make genDES not require an Array
var challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16)); var challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
var response = RFB.genDES(this._rfb_password, challenge); var response = RFB.genDES(this._rfb_credentials.password, challenge);
this._sock.send(response); this._sock.send(response);
this._rfb_init_state = "SecurityResult"; this._rfb_init_state = "SecurityResult";
return true; return true;
@ -1496,7 +1492,6 @@ make_properties(RFB, [
['touchButton', 'rw', 'int'], // Button mask (1, 2, 4) for touch devices (0 means ignore clicks) ['touchButton', 'rw', 'int'], // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
['scale', 'rw', 'float'], // Display area scale factor ['scale', 'rw', 'float'], // Display area scale factor
['viewport', 'rw', 'bool'], // Use viewport clipping ['viewport', 'rw', 'bool'], // Use viewport clipping
['xvp_password_sep', 'rw', 'str'], // Separator for XVP password fields
['disconnectTimeout', 'rw', 'int'], // Time (s) to wait for disconnection ['disconnectTimeout', 'rw', 'int'], // Time (s) to wait for disconnection
['wsProtocols', 'rw', 'arr'], // Protocols to use in the WebSocket connection ['wsProtocols', 'rw', 'arr'], // Protocols to use in the WebSocket connection
['repeaterID', 'rw', 'str'], // [UltraVNC] RepeaterID to connect to ['repeaterID', 'rw', 'str'], // [UltraVNC] RepeaterID to connect to
@ -1506,7 +1501,7 @@ make_properties(RFB, [
['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate): connection state change ['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate): connection state change
['onNotification', 'rw', 'func'], // onNotification(rfb, msg, level, options): notification for the UI ['onNotification', 'rw', 'func'], // onNotification(rfb, msg, level, options): notification for the UI
['onDisconnected', 'rw', 'func'], // onDisconnected(rfb, reason): disconnection finished ['onDisconnected', 'rw', 'func'], // onDisconnected(rfb, reason): disconnection finished
['onPasswordRequired', 'rw', 'func'], // onPasswordRequired(rfb, msg): VNC password is required ['onCredentialsRequired', 'rw', 'func'], // onCredentialsRequired(rfb, types): VNC credentials are required
['onClipboard', 'rw', 'func'], // onClipboard(rfb, text): RFB clipboard contents received ['onClipboard', 'rw', 'func'], // onClipboard(rfb, text): RFB clipboard contents received
['onBell', 'rw', 'func'], // onBell(rfb): RFB Bell message received ['onBell', 'rw', 'func'], // onBell(rfb): RFB Bell message received
['onFBUReceive', 'rw', 'func'], // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed ['onFBUReceive', 'rw', 'func'], // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed

View File

@ -37,7 +37,6 @@ attribute mode is one of the following:
| touchButton | int | RW | 1 | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks. | touchButton | int | RW | 1 | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks.
| scale | float | RW | 1.0 | Display area scale factor | scale | float | RW | 1.0 | Display area scale factor
| viewport | bool | RW | false | Use viewport clipping | viewport | bool | RW | false | Use viewport clipping
| xvp_password_sep | str | RW | '@' | Separator for XVP password fields
| disconnectTimeout | int | RW | 3 | Time (in seconds) to wait for disconnection | disconnectTimeout | int | RW | 3 | Time (in seconds) to wait for disconnection
| wsProtocols | arr | RW | ['binary'] | Protocols to use in the WebSocket connection | wsProtocols | arr | RW | ['binary'] | Protocols to use in the WebSocket connection
| repeaterID | str | RW | '' | UltraVNC RepeaterID to connect to | repeaterID | str | RW | '' | UltraVNC RepeaterID to connect to
@ -50,22 +49,22 @@ In addition to the getter and setter methods to modify configuration
attributes, the RFB object has other methods that are available in the attributes, the RFB object has other methods that are available in the
object instance. object instance.
| name | parameters | description | name | parameters | description
| ------------------ | ------------------------------ | ------------ | ------------------ | ------------------------------- | ------------
| connect | (host, port, password, path) | Connect to the given host:port/path. Optional password and path. | connect | (host, port, credentials, path) | Connect to the given host:port/path. Optional credentials and path.
| disconnect | () | Disconnect | disconnect | () | Disconnect
| sendPassword | (passwd) | Send password after onPasswordRequired callback | sendCredentials | (credentials) | Send credentials after onCredentialsRequired callback
| sendCtrlAltDel | () | Send Ctrl-Alt-Del key sequence | sendCtrlAltDel | () | Send Ctrl-Alt-Del key sequence
| xvpOp | (ver, op) | Send a XVP operation (2=shutdown, 3=reboot, 4=reset) | xvpOp | (ver, op) | Send a XVP operation (2=shutdown, 3=reboot, 4=reset)
| xvpShutdown | () | Send XVP shutdown. | xvpShutdown | () | Send XVP shutdown.
| xvpReboot | () | Send XVP reboot. | xvpReboot | () | Send XVP reboot.
| xvpReset | () | Send XVP reset. | xvpReset | () | Send XVP reset.
| sendKey | (keysym, code, down) | Send a key press event. If down not specified, send a down and up event. | sendKey | (keysym, code, down) | Send a key press event. If down not specified, send a down and up event.
| clipboardPasteFrom | (text) | Send a clipboard paste event | clipboardPasteFrom | (text) | Send a clipboard paste event
| autoscale | (width, height, downscaleOnly) | Scale the display | autoscale | (width, height, downscaleOnly) | Scale the display
| clippingDisplay | () | Check if the remote display is larger than the client display | clippingDisplay | () | Check if the remote display is larger than the client display
| requestDesktopSize | (width, height) | Send a request to change the remote desktop size. | requestDesktopSize | (width, height) | Send a request to change the remote desktop size.
| viewportChangeSize | (width, height) | Change size of the viewport | viewportChangeSize | (width, height) | Change size of the viewport
## 3 Callbacks ## 3 Callbacks
@ -73,19 +72,19 @@ object instance.
The RFB object has certain events that can be hooked with callback The RFB object has certain events that can be hooked with callback
functions. functions.
| name | parameters | description | name | parameters | description
| ------------------ | -------------------------- | ------------ | --------------------- | -------------------------- | ------------
| onUpdateState | (rfb, state, oldstate) | Connection state change (see details below) | onUpdateState | (rfb, state, oldstate) | Connection state change (see details below)
| onNotification | (rfb, msg, level, options) | Notification for the UI (optional options) | onNotification | (rfb, msg, level, options) | Notification for the UI (optional options)
| onDisconnected | (rfb, reason) | Disconnection finished with an optional reason. No reason specified means normal disconnect. | onDisconnected | (rfb, reason) | Disconnection finished with an optional reason. No reason specified means normal disconnect.
| onPasswordRequired | (rfb, msg) | VNC password is required (use sendPassword), optionally comes with a message. | onCredentialsRequired | (rfb, types) | VNC credentials are required (use sendCredentials)
| onClipboard | (rfb, text) | RFB clipboard contents received | onClipboard | (rfb, text) | RFB clipboard contents received
| onBell | (rfb) | RFB Bell message received | onBell | (rfb) | RFB Bell message received
| onFBUReceive | (rfb, fbu) | RFB FBU received but not yet processed (see details below) | onFBUReceive | (rfb, fbu) | RFB FBU received but not yet processed (see details below)
| onFBUComplete | (rfb, fbu) | RFB FBU received and processed (see details below) | onFBUComplete | (rfb, fbu) | RFB FBU received and processed (see details below)
| onFBResize | (rfb, width, height) | Frame buffer (remote desktop) size changed | onFBResize | (rfb, width, height) | Frame buffer (remote desktop) size changed
| onDesktopName | (rfb, name) | VNC desktop name recieved | onDesktopName | (rfb, name) | VNC desktop name recieved
| onXvpInit | (version) | XVP extensions active for this connection. | onXvpInit | (version) | XVP extensions active for this connection.
__RFB onUpdateState callback details__ __RFB onUpdateState callback details__
@ -103,6 +102,20 @@ created for new connections.
| disconnecting | starting to disconnect | disconnecting | starting to disconnect
| disconnected | disconnected - permanent end-state for this RFB object | disconnected | disconnected - permanent end-state for this RFB object
__RFB onCredentialsRequired callback details__
The onCredentialsRequired callback is called when the server requests more
credentials than was specified to connect(). The types argument is a list
of all the credentials that are required. Currently the following are
defined:
| name | description
| -------- | ------------
| username | User that authenticates
| password | Password for user
| target | String specifying target machine or session
__RFB onFBUReceive and on FBUComplete callback details__ __RFB onFBUReceive and on FBUComplete callback details__
The onFBUReceive callback is invoked when a frame buffer update The onFBUReceive callback is invoked when a frame buffer update

View File

@ -104,10 +104,10 @@ RecordingPlayer.prototype = {
this._rfb._sock.close = function () {}; this._rfb._sock.close = function () {};
this._rfb._sock.flush = function () {}; this._rfb._sock.flush = function () {};
this._rfb._checkEvents = function () {}; this._rfb._checkEvents = function () {};
this._rfb.connect = function (host, port, password, path) { this._rfb.connect = function (host, port, credentials, path) {
this._rfb_host = host; this._rfb_host = host;
this._rfb_port = port; this._rfb_port = port;
this._rfb_password = (password !== undefined) ? password : ""; this._rfb_credentials = {};
this._rfb_path = (path !== undefined) ? path : ""; this._rfb_path = (path !== undefined) ? path : "";
this._sock.init('binary', 'ws'); this._sock.init('binary', 'ws');
this._rfb_connection_state = 'connecting'; this._rfb_connection_state = 'connecting';

View File

@ -116,18 +116,18 @@ describe('Remote Frame Buffer Protocol Client', function() {
}); });
}); });
describe('#sendPassword', function () { describe('#sendCredentials', function () {
beforeEach(function () { this.clock = sinon.useFakeTimers(); }); beforeEach(function () { this.clock = sinon.useFakeTimers(); });
afterEach(function () { this.clock.restore(); }); afterEach(function () { this.clock.restore(); });
it('should set the rfb password properly"', function () { it('should set the rfb credentials properly"', function () {
client.sendPassword('pass'); client.sendCredentials({ password: 'pass' });
expect(client._rfb_password).to.equal('pass'); expect(client._rfb_credentials).to.deep.equal({ password: 'pass' });
}); });
it('should call init_msg "soon"', function () { it('should call init_msg "soon"', function () {
client._init_msg = sinon.spy(); client._init_msg = sinon.spy();
client.sendPassword('pass'); client.sendCredentials({ password: 'pass' });
this.clock.tick(5); this.clock.tick(5);
expect(client._init_msg).to.have.been.calledOnce; expect(client._init_msg).to.have.been.calledOnce;
}); });
@ -836,21 +836,22 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._rfb_version = 3.8; client._rfb_version = 3.8;
}); });
it('should call the passwordRequired callback if missing a password', function () { it('should call the onCredentialsRequired callback if missing a password', function () {
client.set_onPasswordRequired(sinon.spy()); client.set_onCredentialsRequired(sinon.spy());
send_security(2, client); send_security(2, client);
var challenge = []; var challenge = [];
for (var i = 0; i < 16; i++) { challenge[i] = i; } for (var i = 0; i < 16; i++) { challenge[i] = i; }
client._sock._websocket._receive_data(new Uint8Array(challenge)); client._sock._websocket._receive_data(new Uint8Array(challenge));
var spy = client.get_onPasswordRequired(); var spy = client.get_onCredentialsRequired();
expect(client._rfb_password.length).to.equal(0); expect(client._rfb_credentials).to.be.empty;
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
expect(spy.args[0][1]).to.have.members(["password"]);
}); });
it('should encrypt the password with DES and then send it back', function () { it('should encrypt the password with DES and then send it back', function () {
client._rfb_password = 'passwd'; client._rfb_credentials = { password: 'passwd' };
send_security(2, client); send_security(2, client);
client._sock._websocket._get_sent_data(); // skip the choice of auth reply client._sock._websocket._get_sent_data(); // skip the choice of auth reply
@ -863,7 +864,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
}); });
it('should transition to SecurityResult immediately after sending the password', function () { it('should transition to SecurityResult immediately after sending the password', function () {
client._rfb_password = 'passwd'; client._rfb_credentials = { password: 'passwd' };
send_security(2, client); send_security(2, client);
var challenge = []; var challenge = [];
@ -886,41 +887,44 @@ describe('Remote Frame Buffer Protocol Client', function() {
}); });
it('should fall through to standard VNC authentication upon completion', function () { it('should fall through to standard VNC authentication upon completion', function () {
client.set_xvp_password_sep('#'); client._rfb_credentials = { username: 'user',
client._rfb_password = 'user#target#password'; target: 'target',
password: 'password' };
client._negotiate_std_vnc_auth = sinon.spy(); client._negotiate_std_vnc_auth = sinon.spy();
send_security(22, client); send_security(22, client);
expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce; expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
}); });
it('should call the passwordRequired callback if the password is missing', function() { it('should call the onCredentialsRequired callback if all credentials are missing', function() {
client.set_onPasswordRequired(sinon.spy()); client.set_onCredentialsRequired(sinon.spy());
client._rfb_password = ''; client._rfb_credentials = {};
send_security(22, client); send_security(22, client);
var spy = client.get_onPasswordRequired(); var spy = client.get_onCredentialsRequired();
expect(client._rfb_password.length).to.equal(0); expect(client._rfb_credentials).to.be.empty;
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
expect(spy.args[0][1]).to.have.members(["username", "password", "target"]);
}); });
it('should call the passwordRequired callback if the password is improperly formatted', function() { it('should call the onCredentialsRequired callback if some credentials are missing', function() {
client.set_onPasswordRequired(sinon.spy()); client.set_onCredentialsRequired(sinon.spy());
client._rfb_password = 'user@target'; client._rfb_credentials = { username: 'user',
target: 'target' };
send_security(22, client); send_security(22, client);
var spy = client.get_onPasswordRequired(); var spy = client.get_onCredentialsRequired();
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
expect(spy.args[0][1]).to.have.members(["username", "password", "target"]);
}); });
it('should split the password, send the first two parts, and pass on the last part', function () { it('should send user and target separately', function () {
client.set_xvp_password_sep('#'); client._rfb_credentials = { username: 'user',
client._rfb_password = 'user#target#password'; target: 'target',
password: 'password' };
client._negotiate_std_vnc_auth = sinon.spy(); client._negotiate_std_vnc_auth = sinon.spy();
send_security(22, client); send_security(22, client);
expect(client._rfb_password).to.equal('password');
var expected = [22, 4, 6]; // auth selection, len user, len target var expected = [22, 4, 6]; // auth selection, len user, len target
for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); } for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }

View File

@ -99,10 +99,7 @@
function updateDesktopName(rfb, name) { function updateDesktopName(rfb, name) {
desktopName = name; desktopName = name;
} }
function passwordRequired(rfb, msg) { function credentials(rfb, types) {
if (typeof msg === 'undefined') {
msg = 'Password Required: ';
}
var html; var html;
var form = document.createElement('form'); var form = document.createElement('form');
@ -115,10 +112,10 @@
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn"); document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
document.getElementById('noVNC_status').innerHTML = ''; document.getElementById('noVNC_status').innerHTML = '';
document.getElementById('noVNC_status').appendChild(form); document.getElementById('noVNC_status').appendChild(form);
document.getElementById('noVNC_status').querySelector('label').textContent = msg; document.getElementById('noVNC_status').querySelector('label').textContent = 'Password Required: ';
} }
function setPassword() { function setPassword() {
rfb.sendPassword(document.getElementById('password_input').value); rfb.sendCredentials({ password: document.getElementById('password_input').value });
return false; return false;
} }
function sendCtrlAltDel() { function sendCtrlAltDel() {
@ -266,7 +263,7 @@
'onUpdateState': updateState, 'onUpdateState': updateState,
'onDisconnected': disconnected, 'onDisconnected': disconnected,
'onXvpInit': xvpInit, 'onXvpInit': xvpInit,
'onPasswordRequired': passwordRequired, 'onCredentialsRequired': credentials,
'onFBUComplete': FBUComplete, 'onFBUComplete': FBUComplete,
'onDesktopName': updateDesktopName}); 'onDesktopName': updateDesktopName});
} catch (exc) { } catch (exc) {
@ -274,7 +271,7 @@
return; // don't continue trying to connect return; // don't continue trying to connect
} }
rfb.connect(host, port, password, path); rfb.connect(host, port, { password: password }, path);
})(); })();
</script> </script>
</head> </head>