Fixed MeshRelay access control
This commit is contained in:
parent
700218c6bd
commit
2a7b0a4f2a
84
meshrelay.js
84
meshrelay.js
|
@ -26,6 +26,24 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
obj.domain = domain;
|
||||
if (obj.remoteaddr.startsWith('::ffff:')) { obj.remoteaddr = obj.remoteaddr.substring(7); }
|
||||
|
||||
// Mesh Rights
|
||||
const MESHRIGHT_EDITMESH = 1;
|
||||
const MESHRIGHT_MANAGEUSERS = 2;
|
||||
const MESHRIGHT_MANAGECOMPUTERS = 4;
|
||||
const MESHRIGHT_REMOTECONTROL = 8;
|
||||
const MESHRIGHT_AGENTCONSOLE = 16;
|
||||
const MESHRIGHT_SERVERFILES = 32;
|
||||
const MESHRIGHT_WAKEDEVICE = 64;
|
||||
const MESHRIGHT_SETNOTES = 128;
|
||||
|
||||
// Site rights
|
||||
const SITERIGHT_SERVERBACKUP = 1;
|
||||
const SITERIGHT_MANAGEUSERS = 2;
|
||||
const SITERIGHT_SERVERRESTORE = 4;
|
||||
const SITERIGHT_FILEACCESS = 8;
|
||||
const SITERIGHT_SERVERUPDATE = 16;
|
||||
const SITERIGHT_LOCKED = 32;
|
||||
|
||||
// Disconnect this agent
|
||||
obj.close = function (arg) {
|
||||
if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Relay: Soft disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket
|
||||
|
@ -70,25 +88,6 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Mark this relay session as authenticated if this is the user end.
|
||||
obj.authenticated = (obj.user != null);
|
||||
|
||||
// Kick off the routing, if we have agent routing instructions, process them here.
|
||||
if ((obj.cookie != null) && (obj.cookie.nodeid != null) && (obj.cookie.tcpport != null) && (obj.cookie.domainid != null)) {
|
||||
// We have routing instructions in the cookie, Send connection request to agent
|
||||
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
|
||||
var command = { nodeid: obj.cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: obj.cookie.tcpport, tcpaddr: obj.cookie.tcpaddr };
|
||||
obj.parent.parent.debug(1, 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
|
||||
if (obj.sendAgentMessage(command, obj.cookie.userid, obj.cookie.domainid) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
|
||||
} else if ((req.query.nodeid != null) && (req.query.tcpport != null)) {
|
||||
// We have routing instructions in the URL arguments, Send connection request to agent
|
||||
if (obj.id == null) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
|
||||
var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) };
|
||||
obj.parent.parent.debug(1, 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
|
||||
if (obj.sendAgentMessage(command, userid, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
|
||||
}
|
||||
performRelay();
|
||||
|
||||
function performRelay() {
|
||||
if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
|
||||
|
@ -128,6 +127,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
obj.ws.send('c'); // Send connect to both peers
|
||||
relayinfo.peer1.ws.send('c');
|
||||
relayinfo.peer1.ws.resume(); // Release the traffic
|
||||
relayinfo.peer2.ws.resume(); // Release the traffic
|
||||
|
||||
relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
|
||||
relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
|
||||
|
@ -198,5 +198,51 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
|
|||
}
|
||||
});
|
||||
|
||||
// Mark this relay session as authenticated if this is the user end.
|
||||
obj.authenticated = (obj.user != null);
|
||||
if (obj.authenticated) {
|
||||
// Kick off the routing, if we have agent routing instructions, process them here.
|
||||
// Routing instructions can only be given by a authenticated user
|
||||
if ((obj.cookie != null) && (obj.cookie.nodeid != null) && (obj.cookie.tcpport != null) && (obj.cookie.domainid != null)) {
|
||||
// We have routing instructions in the cookie, but first, check user access for this node.
|
||||
obj.parent.db.Get(obj.cookie.nodeid, function (err, docs) {
|
||||
if (docs.length == 0) { console.log('ERR: Node not found'); try { obj.close(); } catch (e) { } return; } // Disconnect websocket
|
||||
var node = docs[0];
|
||||
|
||||
// Check if this user has permission to manage this computer
|
||||
var meshlinks = obj.user.links[node.meshid];
|
||||
if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { obj.close(); } catch (e) { } return; }
|
||||
|
||||
// Send connection request to agent
|
||||
if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
|
||||
var command = { nodeid: obj.cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: obj.cookie.tcpport, tcpaddr: obj.cookie.tcpaddr };
|
||||
obj.parent.parent.debug(1, 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
|
||||
if (obj.sendAgentMessage(command, obj.user._id, obj.cookie.domainid) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
|
||||
performRelay();
|
||||
});
|
||||
return obj;
|
||||
} else if ((req.query.nodeid != null) && (req.query.tcpport != null)) {
|
||||
// We have routing instructions in the URL arguments, but first, check user access for this node.
|
||||
obj.parent.db.Get(req.query.nodeid, function (err, docs) {
|
||||
if (docs.length == 0) { console.log('ERR: Node not found'); try { obj.close(); } catch (e) { } return; } // Disconnect websocket
|
||||
var node = docs[0];
|
||||
|
||||
// Check if this user has permission to manage this computer
|
||||
var meshlinks = obj.user.links[node.meshid];
|
||||
if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { obj.close(); } catch (e) { } return; }
|
||||
|
||||
// Send connection request to agent
|
||||
if (obj.id == null) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
|
||||
var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) };
|
||||
obj.parent.parent.debug(1, 'Relay: Sending agent tunnel command: ' + JSON.stringify(command));
|
||||
if (obj.sendAgentMessage(command, obj.user._id, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
|
||||
performRelay();
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is not an authenticated session, or the session does not have routing instructions, just go ahead an connect to existing session.
|
||||
performRelay();
|
||||
return obj;
|
||||
};
|
||||
|
|
36
meshuser.js
36
meshuser.js
|
@ -14,11 +14,12 @@
|
|||
"use strict";
|
||||
|
||||
// Construct a MeshAgent object, called upon connection
|
||||
module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
|
||||
module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, user) {
|
||||
var obj = {};
|
||||
obj.db = db;
|
||||
obj.ws = ws;
|
||||
obj.args = args;
|
||||
obj.user = user;
|
||||
obj.parent = parent;
|
||||
obj.domain = domain;
|
||||
obj.common = parent.common;
|
||||
|
@ -77,27 +78,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
|
|||
|
||||
try {
|
||||
// Check if the user is logged in
|
||||
if ((!req.session) || (!req.session.userid) || (req.session.domainid != domain.id)) {
|
||||
// If a default user is active, setup the session here.
|
||||
if (obj.args.user && obj.parent.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) {
|
||||
if (req.session && req.session.loginmode) { delete req.session.loginmode; }
|
||||
req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
|
||||
req.session.domainid = domain.id;
|
||||
req.session.currentNode = '';
|
||||
} else {
|
||||
// Close the websocket connection
|
||||
console.log('NOAUTH1');
|
||||
ws.send(JSON.stringify({ action: 'close', cause: 'noauth' }));
|
||||
try { obj.ws.close(); } catch (e) { }
|
||||
return;
|
||||
}
|
||||
}
|
||||
req.session.ws = obj.ws; // Associate this websocket session with the web session
|
||||
req.session.ws.userid = req.session.userid;
|
||||
req.session.ws.domainid = domain.id;
|
||||
var user = obj.parent.users[req.session.userid];
|
||||
if (user == null) { try { obj.ws.close(); } catch (e) { } return; }
|
||||
|
||||
// Associate this websocket session with the web session
|
||||
//req.session.ws = obj.ws;
|
||||
//req.session.ws.userid = req.session.userid;
|
||||
//req.session.ws.domainid = domain.id;
|
||||
|
||||
// Add this web socket session to session list
|
||||
obj.ws.sessionId = user._id + '/' + ('' + Math.random()).substring(2);
|
||||
obj.parent.wssessions2[ws.sessionId] = obj.ws;
|
||||
|
@ -141,9 +128,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
|
|||
|
||||
// When data is received from the web socket
|
||||
ws.on('message', function (msg) {
|
||||
var command, user = obj.parent.users[req.session.userid], i = 0, mesh = null, meshid = null, nodeid = null, meshlinks = null, change = 0;
|
||||
var command, i = 0, mesh = null, meshid = null, nodeid = null, meshlinks = null, change = 0;
|
||||
try { command = JSON.parse(msg.toString('utf8')); } catch (e) { return; }
|
||||
if ((user == null) || (obj.common.validateString(command.action, 3, 32) == false)) return; // User must be set and action must be a string between 3 and 32 chars
|
||||
if (obj.common.validateString(command.action, 3, 32) == false) return; // Action must be a string between 3 and 32 chars
|
||||
|
||||
switch (command.action) {
|
||||
case 'ping': { try { ws.send(JSON.stringify({ action: 'pong' })); } catch (ex) { } break; }
|
||||
|
@ -1344,10 +1331,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
|
|||
try { ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: serverinfo })); } catch (ex) { }
|
||||
|
||||
// Send user information to web socket, this is the first thing we send
|
||||
var userinfo = obj.common.Clone(obj.parent.users[req.session.userid]);
|
||||
var userinfo = obj.common.Clone(obj.parent.users[user._id]);
|
||||
delete userinfo.salt;
|
||||
delete userinfo.hash;
|
||||
try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: userinfo })); } catch (ex) { }
|
||||
|
||||
// We are all set, start receiving data
|
||||
ws.resume();
|
||||
} catch (e) { console.log(e); }
|
||||
|
||||
// Read entire file and return it in callback function
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.2.2-i",
|
||||
"version": "0.2.2-k",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
|
|
@ -263,7 +263,9 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
|
|||
if (action == null) return;
|
||||
if (!event) { var event = window.event; }
|
||||
var kc = event.keyCode;
|
||||
if (kc == 0x3B) kc = 0xBA; // ';' key
|
||||
if (kc == 59) kc = 186; // Correct for ';' key in Firefox
|
||||
if (kc == 61) kc = 187; // Correct for '=' key in Firefox
|
||||
if (kc == 173) kc = 189; // Correct for '-' key in Firefox
|
||||
obj.SendKeyMsgKC(action, kc);
|
||||
}
|
||||
|
||||
|
|
|
@ -1270,9 +1270,8 @@
|
|||
}
|
||||
case 'getcookie': {
|
||||
if (message.tag == 'clickonce') {
|
||||
var basicPort = "{{{serverRedirPort}}}"==""?"{{{serverPublicPort}}}":"{{{serverRedirPort}}}";
|
||||
//var rdpurl = "http://" + window.location.hostname + ":" + basicPort + "/clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F" + window.location.hostname + "%2Fmeshrelay.ashx%3Fauth=" + message.cookie + "&CH={{{webcerthash}}}&AP=" + message.protocol + "&HOL=1";
|
||||
var rdpurl = "http://" + window.location.hostname + ":" + basicPort + "/clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F" + window.location.hostname + "%2Fmeshrelay.ashx%3Fauth=" + message.cookie + "&CH={{{webcerthash}}}&AP=" + message.protocol;
|
||||
var basicPort = "{{{serverRedirPort}}}" == "" ? "{{{serverPublicPort}}}" : "{{{serverRedirPort}}}";
|
||||
var rdpurl = "http://" + window.location.hostname + ":" + basicPort + "/clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F" + window.location.hostname + "%2Fmeshrelay.ashx%3Fauth=" + message.cookie + "&CH={{{webcerthash}}}&AP=" + message.protocol + ((debugmode == 1)?"":"&HOL=1");
|
||||
window.open(rdpurl, '_blank');
|
||||
}
|
||||
break;
|
||||
|
|
28
webserver.js
28
webserver.js
|
@ -1063,7 +1063,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
if (!(req.query.host)) { console.log('ERR: No host target specified'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
|
||||
Debug(1, 'Websocket relay connected from ' + user.name + ' for ' + req.query.host + '.');
|
||||
|
||||
ws.pause(); // Hold this socket until we are ready.
|
||||
try { ws._socket.setKeepAlive(true, 240000); } catch (ex) { } // Set TCP keep alive
|
||||
|
||||
// Fetch information about the target
|
||||
|
@ -1104,6 +1103,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
|
||||
// If Intel AMT CIRA connection is available, use it
|
||||
if (((conn & 2) != 0) && (parent.mpsserver.ciraConnections[req.query.host] != null)) {
|
||||
Debug(1, 'Opening relay CIRA channel connection to ' + req.query.host + '.');
|
||||
|
||||
var ciraconn = parent.mpsserver.ciraConnections[req.query.host];
|
||||
|
||||
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
|
||||
|
@ -1114,7 +1115,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
|
||||
// Setup a new CIRA channel
|
||||
if ((port == 16993) || (port == 16995)) {
|
||||
// Perform TLS - ( TODO: THIS IS BROKEN on Intel AMT v7 but works on v10, Not sure why )
|
||||
// Perform TLS - ( TODO: THIS IS BROKEN on Intel AMT v7 but works on v10, Not sure why. Well, could be broken TLS 1.0 in firmware )
|
||||
var ser = new SerialTunnel();
|
||||
var chnl = parent.mpsserver.SetupCiraChannel(ciraconn, port);
|
||||
|
||||
|
@ -1217,7 +1218,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
|
||||
// If Intel AMT direct connection is possible, option a direct socket
|
||||
if ((conn & 4) != 0) { // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
|
||||
Debug(2, 'Opening relay TCP socket connection to ' + req.query.host + '.');
|
||||
Debug(1, 'Opening relay TCP socket connection to ' + req.query.host + '.');
|
||||
|
||||
// When data is received from the web socket, forward the data into the associated TCP connection.
|
||||
ws.on('message', function (msg) {
|
||||
|
@ -1795,7 +1796,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, true, function (ws1, req1, domain, user, cookie) { obj.meshRelayHandler.CreateMeshRelay(obj, ws1, req1, domain, user, cookie); }); });
|
||||
obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
|
||||
obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, handleRelayWebSocket); });
|
||||
obj.app.ws(url + 'control.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, function (ws1, req1, domain, user, cookie) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain); }); });
|
||||
obj.app.ws(url + 'control.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, false, function (ws1, req1, domain, user, cookie) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain, user); }); });
|
||||
|
||||
// Server picture
|
||||
obj.app.get(url + 'serverpic.ashx', function (req, res) {
|
||||
|
@ -1831,6 +1832,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// Authenticates a session and forwards
|
||||
function PerformWSSessionAuth(ws, req, noAuthOk, func) {
|
||||
try {
|
||||
// Hold this websocket until we are ready.
|
||||
ws.pause();
|
||||
|
||||
// Check IP filtering and domain
|
||||
var domain = checkUserIpAddress(ws, req);
|
||||
if (domain == null) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); return; } catch (e) { return; } }
|
||||
|
@ -1843,9 +1847,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// We are authenticated
|
||||
func(ws, req, domain, obj.users[userid]);
|
||||
} else {
|
||||
// If not authenticated, close the websocket connection
|
||||
Debug(1, 'ERR: Websocket bad user/pass auth');
|
||||
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { }
|
||||
// Failed to authenticate, see if a default user is active
|
||||
if (obj.args.user && obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) {
|
||||
// A default user is active
|
||||
func(ws, req, domain, obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]);
|
||||
} else {
|
||||
// If not authenticated, close the websocket connection
|
||||
Debug(1, 'ERR: Websocket bad user/pass auth');
|
||||
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { }
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
@ -1865,9 +1875,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// A default user is active
|
||||
func(ws, req, domain, obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]);
|
||||
return;
|
||||
} else if (req.session && (req.session.userid != null) && (req.session.domainid == domain.id)) {
|
||||
} else if (req.session && (req.session.userid != null) && (req.session.domainid == domain.id) && (obj.users[req.session.userid])) {
|
||||
// This user is logged in using the ExpressJS session
|
||||
func(ws, req, domain, req.session.userid);
|
||||
func(ws, req, domain, obj.users[req.session.userid]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue