mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-24 06:05:53 -05:00
MeshCentral can now remember RDP credentials.
This commit is contained in:
parent
e373cec943
commit
d4ecae73d9
69
apprelays.js
69
apprelays.js
@ -74,17 +74,13 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
||||
obj.relaySocket.on('end', function () { obj.close(); });
|
||||
obj.relaySocket.on('error', function (err) { obj.close(); });
|
||||
|
||||
// Decode the authentication cookie
|
||||
var cookie = parent.parent.decodeCookie(obj.infos.ip, parent.parent.loginCookieEncryptionKey);
|
||||
if (cookie == null) return;
|
||||
|
||||
// Setup the correct URL with domain and use TLS only if needed.
|
||||
var options = { rejectUnauthorized: false };
|
||||
if (domain.dns != null) { options.servername = domain.dns; }
|
||||
var protocol = (args.tlsoffload) ? 'ws' : 'wss';
|
||||
var domainadd = '';
|
||||
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
|
||||
var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + ((cookie.lc == 1) ? 'local' : 'mesh') + 'relay.ashx?noping=1&p=10&auth=' + obj.infos.ip; // Protocol 10 is Web-RDP
|
||||
var url = protocol + '://127.0.0.1:' + args.port + '/' + domainadd + ((obj.cookie.lc == 1) ? 'local' : 'mesh') + 'relay.ashx?noping=1&p=10&auth=' + obj.infos.ip; // Protocol 10 is Web-RDP
|
||||
parent.parent.debug('relay', 'RDP: Connection websocket to ' + url);
|
||||
obj.wsClient = new WebSocket(url, options);
|
||||
obj.wsClient.on('open', function () { parent.parent.debug('relay', 'RDP: Relay websocket open'); });
|
||||
@ -119,6 +115,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
||||
locale: obj.infos.locale
|
||||
}).on('connect', function () {
|
||||
send(['rdp-connect']);
|
||||
if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed
|
||||
}).on('bitmap', function (bitmap) {
|
||||
try { ws.send(bitmap.data); } catch (ex) { } // Send the bitmap data as binary
|
||||
delete bitmap.data;
|
||||
@ -134,13 +131,70 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
||||
}
|
||||
}
|
||||
|
||||
// Save SSH credentials into device
|
||||
function saveRdpCredentials() {
|
||||
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
|
||||
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
|
||||
const node = nodes[0];
|
||||
const changed = (node.rdp == null);
|
||||
|
||||
// Check if credentials are the same
|
||||
if ((typeof node.rdp == 'object') && (node.rdp.d == obj.infos.domain) && (node.rdp.u == obj.infos.username) && (node.rdp.p == obj.infos.password)) return;
|
||||
|
||||
// Save the credentials
|
||||
node.rdp = { d: obj.infos.domain, u: obj.infos.username, p: obj.infos.password };
|
||||
parent.parent.db.Set(node);
|
||||
|
||||
// Event node change if needed
|
||||
if (changed) {
|
||||
// Event the node change
|
||||
var event = { etype: 'node', action: 'changenode', nodeid: obj.nodeid, domain: domain.id, userid: obj.userid, node: parent.CloneSafeNode(node), msg: "Changed RDP credentials" };
|
||||
if (parent.parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
|
||||
parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(node.meshid, [obj.nodeid]), obj, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// When data is received from the web socket
|
||||
// RDP default port is 3389
|
||||
ws.on('message', function (msg) {
|
||||
try {
|
||||
msg = JSON.parse(msg);
|
||||
switch (msg[0]) {
|
||||
case 'infos': { obj.infos = msg[1]; startTcpServer(); break; }
|
||||
case 'infos': {
|
||||
obj.infos = msg[1];
|
||||
|
||||
// Decode the authentication cookie
|
||||
obj.cookie = parent.parent.decodeCookie(obj.infos.ip, parent.parent.loginCookieEncryptionKey);
|
||||
if ((obj.cookie == null) || (typeof obj.cookie.nodeid != 'string') || (typeof obj.cookie.userid != 'string')) return;
|
||||
obj.nodeid = obj.cookie.nodeid;
|
||||
obj.userid = obj.cookie.userid;
|
||||
|
||||
// Check is you need to load server stored credentials
|
||||
if ((typeof obj.infos.options == 'object') && (obj.infos.options.useServerCreds == true)) {
|
||||
parent.parent.db.Get(obj.nodeid, function (err, nodes) {
|
||||
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
|
||||
const node = nodes[0];
|
||||
|
||||
// Check if RDP credentials exist
|
||||
if ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string')) {
|
||||
obj.infos.domain = node.rdp.d;
|
||||
obj.infos.username = node.rdp.u;
|
||||
obj.infos.password = node.rdp.p;
|
||||
startTcpServer();
|
||||
} else {
|
||||
// No server credentials.
|
||||
obj.infos.domain = '';
|
||||
obj.infos.username = '';
|
||||
obj.infos.password = '';
|
||||
startTcpServer();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
startTcpServer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'mouse': { if (rdpClient) { rdpClient.sendPointerEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
|
||||
case 'wheel': { if (rdpClient) { rdpClient.sendWheelEvent(msg[1], msg[2], msg[3], msg[4]); } break; }
|
||||
case 'scancode': { if (rdpClient) { rdpClient.sendKeyEventScancode(msg[1], msg[2]); } break; }
|
||||
@ -417,6 +471,9 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
|
||||
const node = nodes[0];
|
||||
const changed = (node.ssh == null);
|
||||
|
||||
// Check if credentials are the same
|
||||
if ((typeof node.ssh == 'object') && (node.ssh.u == obj.username) && (node.ssh.p == obj.password)) return;
|
||||
|
||||
// Save the credentials
|
||||
node.ssh = { u: obj.username, p: obj.password };
|
||||
parent.parent.db.Set(node);
|
||||
|
@ -713,6 +713,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
// Remove SSH credentials if present
|
||||
if (docs[i].ssh != null) { docs[i].ssh = 1; }
|
||||
|
||||
// Remove RDP credentials if present
|
||||
if (docs[i].rdp != null) { docs[i].rdp = 1; }
|
||||
|
||||
// Remove Intel AMT credential if present
|
||||
if (docs[i].intelamt != null) {
|
||||
if (docs[i].intelamt.pass != null) { docs[i].intelamt.pass = 1; }
|
||||
@ -4275,6 +4278,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
if (node.ssh != null) { delete node.ssh; change = 1; changes.push('ssh'); } // Delete the SSH cendentials
|
||||
}
|
||||
|
||||
if ((typeof command.rdp == 'number') && (command.rdp == 0)) {
|
||||
if (node.rdp != null) { delete node.rdp; change = 1; changes.push('rdp'); } // Delete the RDP cendentials
|
||||
}
|
||||
|
||||
if (domain.geolocation && command.userloc && ((node.userloc == null) || (command.userloc[0] != node.userloc[0]) || (command.userloc[1] != node.userloc[1]))) {
|
||||
change = 1;
|
||||
if ((command.userloc.length == 0) && (node.userloc)) {
|
||||
|
@ -147,7 +147,7 @@
|
||||
* @param password {string} session password
|
||||
* @param next {function} asynchrone end callback
|
||||
*/
|
||||
connect : function (ip, domain, username, password, next) {
|
||||
connect : function (ip, domain, username, password, options, next) {
|
||||
// Start connection
|
||||
var self = this;
|
||||
this.socket = new WebSocket('wss://' + window.location.host + '/mstscrelay.ashx');
|
||||
@ -164,6 +164,7 @@
|
||||
domain: domain,
|
||||
username: username,
|
||||
password: password,
|
||||
options: options,
|
||||
locale: Mstsc.locale()
|
||||
}]));
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1876,6 +1876,7 @@
|
||||
node.gpsloc = message.event.node.gpsloc;
|
||||
node.tags = message.event.node.tags;
|
||||
node.ssh = message.event.node.ssh;
|
||||
node.rdp = message.event.node.rdp;
|
||||
node.userloc = message.event.node.userloc;
|
||||
node.rdpport = message.event.node.rdpport;
|
||||
node.rfbport = message.event.node.rfbport;
|
||||
@ -3430,13 +3431,17 @@
|
||||
x += addDeviceAttribute("Tags", '<span style=color:black>' + groupingTags + '</span>');
|
||||
}
|
||||
|
||||
// SSH Credentials
|
||||
if (node.ssh != null) {
|
||||
// SSH & RDP Credentials
|
||||
if ((node.ssh != null) || (node.rdp != null)) {
|
||||
var y = [];
|
||||
if ((meshrights & 4) != 0) {
|
||||
x += addDeviceAttribute("Credentials", '<span onclick=showClearSshDialog(3) style=cursor:pointer>' + "SSH" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>');
|
||||
if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + "SSH" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
|
||||
if (node.rdp != null) { y.push('<span onclick=showClearRdpDialog(3) style=cursor:pointer>' + "RDP" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
|
||||
} else {
|
||||
x += addDeviceAttribute("Credentials", "SSH");
|
||||
if (node.ssh != null) { y.push("SSH"); }
|
||||
if (node.rdp != null) { y.push("RDP"); }
|
||||
}
|
||||
x += addDeviceAttribute("Credentials", y.join(', '));
|
||||
}
|
||||
|
||||
x += '</table><br />';
|
||||
@ -3842,6 +3847,8 @@
|
||||
|
||||
function showClearSshDialog() { setDialogMode(2, "Edit Device", 3, showClearSshDialogEx, "Clear SSH credentials?"); }
|
||||
function showClearSshDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, ssh: 0 }); }
|
||||
function showClearRdpDialog() { setDialogMode(2, "Edit Device", 3, showClearRdpDialogEx, "Clear RDP credentials?"); }
|
||||
function showClearRdpDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, rdp: 0 }); }
|
||||
|
||||
var showEditNodeValueDialog_modes = ["Device Name", "Hostname", "Description", "Tags"];
|
||||
var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc', 'tags'];
|
||||
|
@ -3000,6 +3000,7 @@
|
||||
node.gpsloc = message.event.node.gpsloc;
|
||||
node.tags = message.event.node.tags;
|
||||
node.ssh = message.event.node.ssh;
|
||||
node.rdp = message.event.node.rdp;
|
||||
node.userloc = message.event.node.userloc;
|
||||
node.rdpport = message.event.node.rdpport;
|
||||
node.rfbport = message.event.node.rfbport;
|
||||
@ -6506,13 +6507,17 @@
|
||||
x += addDeviceAttribute("Tags", groupingTags);
|
||||
}
|
||||
|
||||
// SSH Credentials
|
||||
if (node.ssh != null) {
|
||||
// SSH & RDP Credentials
|
||||
if ((node.ssh != null) || (node.rdp != null)) {
|
||||
var y = [];
|
||||
if ((meshrights & 4) != 0) {
|
||||
x += addDeviceAttribute("Credentials", '<span onclick=showClearSshDialog(3) style=cursor:pointer>' + "SSH" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>');
|
||||
if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + "SSH" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
|
||||
if (node.rdp != null) { y.push('<span onclick=showClearRdpDialog(3) style=cursor:pointer>' + "RDP" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
|
||||
} else {
|
||||
x += addDeviceAttribute("Credentials", "SSH");
|
||||
if (node.ssh != null) { y.push("SSH"); }
|
||||
if (node.rdp != null) { y.push("RDP"); }
|
||||
}
|
||||
x += addDeviceAttribute("Credentials", y.join(', '));
|
||||
}
|
||||
|
||||
x += '</table><br />';
|
||||
@ -7558,6 +7563,8 @@
|
||||
|
||||
function showClearSshDialog() { setDialogMode(2, "Edit Device", 3, showClearSshDialogEx, "Clear SSH credentials?"); }
|
||||
function showClearSshDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, ssh: 0 }); }
|
||||
function showClearRdpDialog() { setDialogMode(2, "Edit Device", 3, showClearRdpDialogEx, "Clear RDP credentials?"); }
|
||||
function showClearRdpDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, rdp: 0 }); }
|
||||
|
||||
var showEditNodeValueDialog_modes = ["Device Name", "Hostname", "Description", "Tags"];
|
||||
var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc', 'tags'];
|
||||
|
@ -44,6 +44,10 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.formDropdown {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.formLabel { }
|
||||
|
||||
.formControl {
|
||||
@ -76,6 +80,7 @@
|
||||
var urlargs = parseUriArgs();
|
||||
if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; }
|
||||
var cookie = '{{{cookie}}}';
|
||||
var serverCredentials = (decodeURIComponent('{{{serverCredentials}}}') == 'true');
|
||||
var name = decodeURIComponent('{{{name}}}');
|
||||
if (name != '') { document.title = name + ' - ' + document.title; }
|
||||
|
||||
@ -90,6 +95,15 @@
|
||||
QE('inputPassword', false);
|
||||
QE('connectButton', false);
|
||||
}
|
||||
|
||||
if (serverCredentials == true) {
|
||||
QV('dropdowndomain', true);
|
||||
Q('d3coreMode').value = 1;
|
||||
} else {
|
||||
QV('dropdowndomain', false);
|
||||
Q('d3coreMode').value = 2;
|
||||
}
|
||||
dropDownChange();
|
||||
}
|
||||
|
||||
function connect(domain, username, password) {
|
||||
@ -97,11 +111,13 @@
|
||||
var domain = Q('inputDomain').value;
|
||||
var username = Q('inputUsername').value;
|
||||
var password = Q('inputPassword').value;
|
||||
var savepass = Q('inputSaveCredentials').checked;
|
||||
var options = { savepass: savepass, useServerCreds: (Q('d3coreMode').value == 1) };
|
||||
QV('myCanvas', true);
|
||||
QV('main', false);
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
client.connect(cookie, domain, username, password, function (err) { QV('myCanvas', false); QV('main', true); });
|
||||
client.connect(cookie, domain, username, password, options, function (err) { QV('myCanvas', false); QV('main', true); });
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -137,6 +153,16 @@
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function dropDownChange() {
|
||||
var newCreds = (Q('d3coreMode').value == 2);
|
||||
QV('rowdomain', newCreds);
|
||||
QV('rowusername', newCreds);
|
||||
QV('rowpassword', newCreds);
|
||||
QV('rowremember', newCreds);
|
||||
if (newCreds) Q('inputUsername').focus();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload='load()' style="position:absolute;top:0px;right:0;left:0;bottom:0px">
|
||||
@ -147,18 +173,27 @@
|
||||
<tr>
|
||||
<td colspan="2"><hr style="color:gray;border:1px solid;" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr id="dropdowndomain" style="display:none">
|
||||
<td colspan="2">
|
||||
<select id=d3coreMode style=width:100%;margin-bottom:5px class="formDropdown" onchange="dropDownChange()"><option value=1>Use server credentals</option><option value=2>Use new credentals</option></select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="rowdomain" style="display:none">
|
||||
<td><label for="inputDomain" class="formLabel">Domain</label></td>
|
||||
<td style="text-align:right"><input type="text" id="inputDomain" class="formControl" placeholder="Domain"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr id="rowusername" style="display:none">
|
||||
<td><label for="inputUsername" class="formLabel">Username</label></td>
|
||||
<td style="text-align:right"><input type="text" id="inputUsername" class="formControl" placeholder="Username"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr id="rowpassword" style="display:none">
|
||||
<td><label for="inputPassword" class="formLabel">Password</label></td>
|
||||
<td style="text-align:right"><input type="password" id="inputPassword" class="formControl" placeholder="Password"></td>
|
||||
</tr>
|
||||
<tr id="rowremember" style="display:none">
|
||||
<td></td>
|
||||
<td><label><input type="checkbox" id="inputSaveCredentials" style="margin-left:8px;margin-right:5px">Remember credentials</label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><button class="connectButton" onclick="return connect()">Connect</button></td>
|
||||
</tr>
|
||||
|
20
webserver.js
20
webserver.js
@ -1870,7 +1870,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
// This is a query with a websocket relay cookie, check that the cookie is valid and use it.
|
||||
var rcookie = parent.decodeCookie(req.query.ws, parent.loginCookieEncryptionKey, 60); // Cookie with 1 hour timeout
|
||||
if ((rcookie != null) && (rcookie.domainid == domain.id) && (rcookie.nodeid != null) && (rcookie.tcpport != null)) {
|
||||
render(req, res, getRenderPage(page, req, domain), getRenderArgs({ cookie: req.query.ws, name: encodeURIComponent(req.query.name).replace(/'/g, '%27') }, req, domain)); return;
|
||||
|
||||
// Fetch the node from the database
|
||||
obj.db.Get(rcookie.nodeid, function (err, nodes) {
|
||||
if ((err != null) || (nodes.length != 1)) { res.sendStatus(404); return; }
|
||||
const node = nodes[0];
|
||||
|
||||
// Check if we have RDP credentials for this device
|
||||
var serverCredentials = ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string'));
|
||||
|
||||
// Render the page
|
||||
render(req, res, getRenderPage(page, req, domain), getRenderArgs({ cookie: req.query.ws, name: encodeURIComponent(req.query.name).replace(/'/g, '%27'), serverCredentials: serverCredentials }, req, domain));
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1912,6 +1924,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if ((err != null) || (nodes.length != 1)) { res.sendStatus(404); return; }
|
||||
const node = nodes[0];
|
||||
|
||||
// Check if we have RDP credentials for this device
|
||||
var serverCredentials = ((typeof node.rdp == 'object') && (typeof node.rdp.d == 'string') && (typeof node.rdp.u == 'string') && (typeof node.rdp.p == 'string'));
|
||||
|
||||
// Check access rights, must have remote control rights
|
||||
if ((obj.GetNodeRights(user, node.meshid, node._id) & MESHRIGHT_REMOTECONTROL) == 0) { res.sendStatus(401); return; }
|
||||
|
||||
@ -1930,7 +1945,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Generate a cookie and respond
|
||||
var cookie = parent.encodeCookie({ userid: user._id, domainid: user.domain, nodeid: node._id, tcpport: port }, parent.loginCookieEncryptionKey);
|
||||
render(req, res, getRenderPage(page, req, domain), getRenderArgs({ cookie: cookie, name: encodeURIComponent(node.name).replace(/'/g, '%27') }, req, domain));
|
||||
render(req, res, getRenderPage(page, req, domain), getRenderArgs({ cookie: cookie, name: encodeURIComponent(node.name).replace(/'/g, '%27'), serverCredentials: serverCredentials }, req, domain));
|
||||
});
|
||||
}
|
||||
|
||||
@ -7030,6 +7045,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
r = Object.assign({}, r); // Shallow clone
|
||||
if (r.pmt != null) { r.pmt = 1; }
|
||||
if (r.ssh != null) { r.ssh = 1; }
|
||||
if (r.rdp != null) { r.rdp = 1; }
|
||||
if ((r.intelamt != null) && ((r.intelamt.pass != null) || (r.intelamt.mpspass != null))) {
|
||||
r.intelamt = Object.assign({}, r.intelamt); // Shallow clone
|
||||
if (r.intelamt.pass != null) { r.intelamt.pass = 1; }; // Remove the Intel AMT administrator password from the node
|
||||
|
Loading…
Reference in New Issue
Block a user