Improved meshrelay security.
This commit is contained in:
parent
63813a48ff
commit
cdfcb01cfa
24
meshrelay.js
24
meshrelay.js
|
@ -18,8 +18,18 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
obj.ws = ws;
|
||||
obj.id = req.query.id;
|
||||
obj.user = user;
|
||||
obj.ruserid = null;
|
||||
obj.req = req; // Used in multi-server.js
|
||||
|
||||
// Check relay authentication
|
||||
if ((user == null) && (req.query.rauth != null)) {
|
||||
var rcookie = parent.parent.decodeCookie(req.query.rauth, parent.parent.loginCookieEncryptionKey, 240); // Cookie with 4 hour timeout
|
||||
if (rcookie.ruserid != null) { obj.ruserid = rcookie.ruserid; }
|
||||
}
|
||||
|
||||
// If there is no authentication, drop this connection
|
||||
if ((obj.id.startsWith('meshmessenger/') == false) && (obj.user == null) && (obj.ruserid == null)) { try { ws.close(); parent.parent.debug('relay', 'Relay: Connection with no authentication (' + cleanRemoteAddr(req.ip) + ')'); } catch (e) { console.log(e); } return; }
|
||||
|
||||
// Relay session count (we may remove this in the future)
|
||||
obj.relaySessionCounted = true;
|
||||
parent.relaySessionCount++;
|
||||
|
@ -150,6 +160,20 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
return null;
|
||||
}
|
||||
|
||||
// Check that both connection are for the same user
|
||||
if (!obj.id.startsWith('meshmessenger/')) {
|
||||
var u1 = obj.user ? obj.user._id : obj.ruserid;
|
||||
var u2 = relayinfo.peer1.user ? relayinfo.peer1.user._id : relayinfo.peer1.ruserid;
|
||||
if (u1 != u2) {
|
||||
ws.close();
|
||||
parent.parent.debug('relay', 'Relay auth mismatch: ' + obj.id + ' (' + cleanRemoteAddr(req.ip) + ')');
|
||||
delete obj.id;
|
||||
delete obj.ws;
|
||||
delete obj.peer;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to peer
|
||||
obj.peer = relayinfo.peer1;
|
||||
obj.peer.peer = obj;
|
||||
|
|
|
@ -361,7 +361,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
case 'authcookie':
|
||||
{
|
||||
// Renew the authentication cookie
|
||||
try { ws.send(JSON.stringify({ action: 'authcookie', cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { }
|
||||
try {
|
||||
console.log(req.ip);
|
||||
ws.send(JSON.stringify({
|
||||
action: 'authcookie',
|
||||
cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, parent.parent.loginCookieEncryptionKey),
|
||||
rcookie: parent.parent.encodeCookie({ ruserid: user._id }, parent.parent.loginCookieEncryptionKey)
|
||||
}));
|
||||
} catch (ex) { }
|
||||
break;
|
||||
}
|
||||
case 'logincookie':
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.4.2-k",
|
||||
"version": "0.4.2-l",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
*/
|
||||
|
||||
// Construct a MeshServer agent direction object
|
||||
var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, authCookie, domainUrl) {
|
||||
var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, authCookie, rauthCookie, domainUrl) {
|
||||
var obj = {};
|
||||
obj.m = module; // This is the inner module (Terminal or Desktop)
|
||||
module.parent = obj;
|
||||
obj.meshserver = meshserver;
|
||||
obj.authCookie = authCookie;
|
||||
obj.rauthCookie = rauthCookie;
|
||||
obj.State = 0;
|
||||
obj.nodeid = null;
|
||||
obj.socket = null;
|
||||
|
@ -49,7 +50,9 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
|
|||
obj.socket.onclose = obj.xxOnSocketClosed;
|
||||
obj.xxStateChange(1);
|
||||
//obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: url2 });
|
||||
obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: "*" + domainUrl + "meshrelay.ashx?p=" + obj.protocol + "&nodeid=" + nodeid + "&id=" + obj.tunnelid, usage: obj.protocol });
|
||||
var rurl = "*" + domainUrl + "meshrelay.ashx?p=" + obj.protocol + "&nodeid=" + nodeid + "&id=" + obj.tunnelid;
|
||||
if ((rauthCookie != null) && (rauthCookie != '')) { rurl += '&rauth=' + rauthCookie; }
|
||||
obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: rurl, usage: obj.protocol });
|
||||
//obj.debug("Agent Redir Start: " + url);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -603,6 +603,8 @@
|
|||
var domain = "{{{domain}}}";
|
||||
var domainUrl = "{{{domainurl}}}";
|
||||
var authCookie = "{{{authCookie}}}";
|
||||
var authRelayCookie = "{{{authRelayCookie}}}";
|
||||
var authCookieRenewTimer = null;
|
||||
var meshserver = null;
|
||||
var xdr = null;
|
||||
var serverinfo = null;
|
||||
|
@ -658,13 +660,14 @@
|
|||
if (errorCode == 'noauth') { QH('p0span', 'Unable to perform authentication'); return; }
|
||||
if (prevState == 2) { setTimeout(serverPoll, 5000); } else { QH('p0span', 'Unable to connect web socket'); }
|
||||
// Clean up here
|
||||
|
||||
if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; }
|
||||
} else if (state == 2) {
|
||||
// Fetch list of meshes, nodes, files
|
||||
meshserver.send({ action: 'meshes' });
|
||||
meshserver.send({ action: 'nodes' });
|
||||
meshserver.send({ action: 'files' });
|
||||
if (xxcurrentView < 2) { go(2); }
|
||||
authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
|
||||
}
|
||||
QV('topMenuIcon', state == 2);
|
||||
}
|
||||
|
@ -715,6 +718,12 @@
|
|||
QV('logoutMenuOption', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide logout if in single user mode or domain authentication
|
||||
break;
|
||||
}
|
||||
case 'authcookie': {
|
||||
// Got an authentication cookie refresh
|
||||
authCookie = message.cookie;
|
||||
authRelayCookie = message.rcookie;
|
||||
break;
|
||||
}
|
||||
case 'userinfo': {
|
||||
userinfo = message.userinfo;
|
||||
QH('p3userName', userinfo.name);
|
||||
|
@ -2312,7 +2321,7 @@
|
|||
desktop.contype = 2;
|
||||
} else {
|
||||
// Setup the Mesh Agent remote desktop
|
||||
desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, domainUrl);
|
||||
desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
desktop.debugmode = debugmode;
|
||||
desktop.m.debugmode = debugmode;
|
||||
desktop.attemptWebRTC = attemptWebRTC;
|
||||
|
@ -2670,7 +2679,7 @@
|
|||
function connectFiles(e) {
|
||||
if (!files) {
|
||||
// Setup a mesh agent files
|
||||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, domainUrl);
|
||||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
files.attemptWebRTC = attemptWebRTC;
|
||||
files.onStateChanged = onFilesStateChange;
|
||||
files.Start(filesNode._id);
|
||||
|
@ -2896,7 +2905,7 @@
|
|||
// Called by the html page to start a download, arguments are: path, file name and file size.
|
||||
function p13downloadfile(x, y, z) {
|
||||
if (xxdialogMode) return;
|
||||
downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, domainUrl); // Create our websocket file transport
|
||||
downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); // Create our websocket file transport
|
||||
downloadFile.ctrlMsgAllowed = false;
|
||||
downloadFile.onStateChanged = onFileDownloadStateChange;
|
||||
downloadFile.xpath = decodeURIComponent(x);
|
||||
|
@ -2983,7 +2992,7 @@
|
|||
|
||||
// Connect again
|
||||
function p13uploadReconnect() {
|
||||
uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, domainUrl);
|
||||
uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
uploadFile.ws.attemptWebRTC = false;
|
||||
uploadFile.ws.ctrlMsgAllowed = false;
|
||||
uploadFile.ws.onStateChanged = onFileUploadStateChange;
|
||||
|
|
|
@ -184,9 +184,9 @@
|
|||
<label><input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span></label>
|
||||
</td>
|
||||
<td id=kvmListToolbar class=style14 style="display:none">
|
||||
<input type="button" onclick="connectAllKvmFunction()" value="Connect All" />
|
||||
<span id="kvmMultiConnectButtonSpan"><input type="button" onclick="connectAllKvmFunction()" value="Connect All" /> </span>
|
||||
<input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All" />
|
||||
<label><input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto </label>
|
||||
<span id="kvmAutoConnectButtonSpan"><label><input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto </label></span>
|
||||
<input type="button" onclick="showMultiDesktopSettings()" value="Settings" />
|
||||
</td>
|
||||
<td id=devMapToolbar class=style14 style="display:none">
|
||||
|
@ -1017,6 +1017,7 @@
|
|||
var domain = "{{{domain}}}";
|
||||
var domainUrl = "{{{domainurl}}}";
|
||||
var authCookie = "{{{authCookie}}}";
|
||||
var authRelayCookie = "{{{authRelayCookie}}}";
|
||||
var authCookieRenewTimer = null;
|
||||
var multiDesktop = {};
|
||||
var multiDesktopFilter = null;
|
||||
|
@ -1466,6 +1467,7 @@
|
|||
case 'authcookie': {
|
||||
// Got an authentication cookie refresh
|
||||
authCookie = message.cookie;
|
||||
authRelayCookie = message.rcookie;
|
||||
break;
|
||||
}
|
||||
case 'serverinfo': {
|
||||
|
@ -2664,6 +2666,10 @@
|
|||
if (typeof deviceHeaderCount[node.state] == 'undefined') { deviceHeaderCount[node.state] = 1; } else { deviceHeaderCount[node.state]++; }
|
||||
}
|
||||
|
||||
// Display "connect all" and "auto"
|
||||
QV('kvmMultiConnectButtonSpan', (kvmDivs.length < 16));
|
||||
QV('kvmAutoConnectButtonSpan', (kvmDivs.length < 16));
|
||||
|
||||
// If displaying devices by groups, sort the group names and display the devices.
|
||||
if (sort == 3) {
|
||||
if (view == 2) { r = '<table style=width:100%;margin-top:4px cellpadding=0 cellspacing=0><th style=color:gray><th style=color:gray;width:120px>User<th style=color:gray;width:120px>Address<th style=color:gray;width:100px>Connectivity'; }
|
||||
|
@ -2877,7 +2883,7 @@
|
|||
multiDesktop[nodeid] = desk;
|
||||
} else if (contype == 1) {
|
||||
// Setup the Mesh Agent remote desktop
|
||||
desk = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('kvmid_' + shortid), serverPublicNamePort, authCookie, domainUrl);
|
||||
desk = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('kvmid_' + shortid), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
desk.shortid = shortid;
|
||||
desk.attemptWebRTC = attemptWebRTC;
|
||||
desk.onStateChanged = onMultiDesktopStateChange;
|
||||
|
@ -5038,7 +5044,7 @@
|
|||
desktop.contype = 2;
|
||||
} else {
|
||||
// Setup the Mesh Agent remote desktop
|
||||
desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, domainUrl);
|
||||
desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
desktop.debugmode = debugmode;
|
||||
desktop.m.debugmode = debugmode;
|
||||
desktop.attemptWebRTC = attemptWebRTC;
|
||||
|
@ -5755,7 +5761,7 @@
|
|||
}
|
||||
}
|
||||
if ((e && (e.shiftKey == true)) || (options && (options.powershell))) { termoptions.protocol = 6; }
|
||||
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term', termoptions), serverPublicNamePort, authCookie, domainUrl);
|
||||
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term', termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
terminal.debugmode = debugmode;
|
||||
terminal.m.debugmode = debugmode;
|
||||
terminal.m.onTitleChange = function (sender, title) { QH('termtitle', ' - ' + EscapeHtml(title)); }
|
||||
|
@ -5891,7 +5897,7 @@
|
|||
p13clearConsoleMsg();
|
||||
if (!files) {
|
||||
// Setup a mesh agent files
|
||||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, domainUrl);
|
||||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
files.attemptWebRTC = attemptWebRTC;
|
||||
files.onStateChanged = onFilesStateChange;
|
||||
files.onConsoleMessageChange = function () {
|
||||
|
@ -6188,7 +6194,7 @@
|
|||
// Called by the html page to start a download, arguments are: path, file name and file size.
|
||||
function p13downloadfile(x, y, z) {
|
||||
if (xxdialogMode) return;
|
||||
downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, domainUrl); // Create our websocket file transport
|
||||
downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); // Create our websocket file transport
|
||||
downloadFile.ctrlMsgAllowed = false;
|
||||
downloadFile.onStateChanged = onFileDownloadStateChange;
|
||||
downloadFile.xpath = decodeURIComponent(x);
|
||||
|
@ -6297,7 +6303,7 @@
|
|||
|
||||
// Connect again
|
||||
function p13uploadReconnect() {
|
||||
uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, domainUrl);
|
||||
uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
||||
uploadFile.ws.attemptWebRTC = false;
|
||||
uploadFile.ws.ctrlMsgAllowed = false;
|
||||
uploadFile.ws.onStateChanged = onFileUploadStateChange;
|
||||
|
|
|
@ -1510,6 +1510,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
|
||||
// Create a authentication cookie
|
||||
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id, ip: cleanRemoteAddr(req.ip) }, obj.parent.loginCookieEncryptionKey);
|
||||
const authRelayCookie = obj.parent.encodeCookie({ ruserid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
|
||||
|
||||
// Send the master web application
|
||||
if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // If a default user is in use or no user mode, don't display the logout button
|
||||
|
@ -1522,7 +1523,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
parent.debug('web', 'handleRootRequestEx: success.');
|
||||
obj.db.Get('ws' + user._id, function (err, states) {
|
||||
var webstate = (states.length == 1) ? states[0].state : '';
|
||||
res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: (parent.pluginHandler == null)?'null':parent.pluginHandler.prepExports() });
|
||||
res.render(getRenderPage('default', req), { authCookie: authCookie, authRelayCookie: authRelayCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: (parent.pluginHandler == null)?'null':parent.pluginHandler.prepExports() });
|
||||
});
|
||||
} else {
|
||||
// Send back the login application
|
||||
|
@ -3460,7 +3461,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// This is a encrypted cookie authentication
|
||||
var cookie = obj.parent.decodeCookie(req.query.auth, obj.parent.loginCookieEncryptionKey, 240); // Cookie with 4 hour timeout
|
||||
if ((cookie == null) && (obj.parent.multiServer != null)) { cookie = obj.parent.decodeCookie(req.query.auth, obj.parent.serverKey, 240); } // Try the server key
|
||||
//if ((cookie != null) && (cookie.ip != null) && (cookie.ip != cleanRemoteAddr(req.ip))) { cookie = null; } // If the cookie if binded to an IP address, check here.
|
||||
if ((cookie != null) && (cookie.ip != null) && (cookie.ip != cleanRemoteAddr(req.ip) && (cookie.ip != req.ip))) { cookie = null; } // If the cookie if binded to an IP address, check here.
|
||||
if ((cookie != null) && (obj.users[cookie.userid]) && (cookie.domainid == domain.id)) {
|
||||
// Valid cookie, we are authenticated
|
||||
func(ws, req, domain, obj.users[cookie.userid], cookie);
|
||||
|
|
Loading…
Reference in New Issue