Added 2FA support in MeshCMD.

Ylian Saint-Hilaire committed Apr 3, 2020 at 16:06 UTC 459540f7427e734d0d283793d1b333ff920abe06
7 files changed +81 -18
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/meshcmd.js
+77 -14
@@ -153,8 +153,10 @@ function run(argv) {
153 if ((typeof args.cdrom) == 'string') { settings.cdrom = args.cdrom; }
154 if ((typeof args.tag) == 'string') { settings.tag = args.tag; }
155 if ((typeof args.scan) == 'string') { settings.scan = args.scan; }
156 + if ((typeof args.token) == 'string') { settings.token = args.token; }
157 if ((typeof args.timeout) == 'string') { settings.timeout = parseInt(args.timeout); }
158 if ((typeof args.uuidoutput) == 'string' || args.uuidoutput) { settings.uuidoutput = args.uuidoutput; }
159 + if (args.emailtoken) { settings.emailtoken = true; }
160 if (args.debug === true) { settings.debuglevel = 1; }
161 if (args.debug) { try { waitForDebugger(); } catch (e) { } }
162 if (args.noconsole) { settings.noconsole = true; }
@@ -2048,20 +2050,80 @@ function processLmsControlData(data) {
2050 //
2051
2052 function startRouter() {
2053 + // Start by requesting a login token, this is needed because of 2FA and check that we have correct credentials from the start
2054 + var options;
2055 + try {
2056 + var url = settings.serverurl.split('meshrelay.ashx').join('control.ashx') + '?user=' + settings.username + '&pass=' + settings.password;
2057 + if (settings.emailtoken) { url += '&token=**email**'; } else if (settings.token != null) { url += '&token=' + settings.token; }
2058 + options = http.parseUri(url);
2059 + } catch (e) { console.log("Unable to parse \"serverUrl\"."); process.exit(1); return; }
2060 + options.checkServerIdentity = onVerifyServer;
2061 + options.rejectUnauthorized = false;
2062 + settings.websocket = http.request(options);
2063 + settings.websocket.upgrade = OnServerWebSocket;
2064 + settings.websocket.on('error', function (e) { console.log("ERROR: " + JSON.stringify(e)); });
2065 + settings.websocket.end();
2066 +}
2067 +
2068 +function OnServerWebSocket(msg, s, head) {
2069 + settings.webchannel = s;
2070 + s.on('data', function (msg) {
2071 + var command = JSON.parse(msg);
2072 + switch (command.action) {
2073 + case 'close': {
2074 + if (command.cause == 'noauth') {
2075 + if (command.msg == 'tokenrequired') {
2076 + if (command.email2fasent === true) {
2077 + console.log("Login token email sent.");
2078 + } else if (command.email2fa === true) {
2079 + console.log("Login token required, use --token [token], or --emailtoken get a token.");
2080 + } else {
2081 + console.log("Login token required, use --token [token].");
2082 + }
2083 + } else { console.log("Invalid username or password."); }
2084 + } else { console.log("Server disconnected: " + command.msg); }
2085 + process.exit(1);
2086 + return;
2087 + }
2088 + case 'serverinfo': {
2089 + s.write("{\"action\":\"authcookie\"}"); // Ask for our first authentication cookie
2090 + break;
2091 + }
2092 + case 'authcookie': {
2093 + if (settings.acookie == null) {
2094 + settings.acookie = command.cookie;
2095 + settings.rcookie = command.rcookie;
2096 + settings.renewCookieTimer = setInterval(function () { settings.webchannel.write("{\"action\":\"authcookie\"}"); }, 600000); // Ask for new cookie every 10 minutes
2097 + startRouterEx();
2098 + } else {
2099 + settings.acookie = command.cookie;
2100 + settings.rcookie = command.rcookie;
2101 + }
2102 + break;
2103 + }
2104 + }
2105 + });
2106 + s.on('error', function () { console.log("Server connection error."); process.exit(1); return; });
2107 + s.on('close', function () { console.log("Server closed the connection."); process.exit(1); return; });
2108 +}
2109 +
2110 +function startRouterEx() {
2111 tcpserver = net.createServer(OnTcpClientConnected);
2112 tcpserver.on('error', function (e) { console.log("ERROR: " + JSON.stringify(e)); exit(0); return; });
2053 - tcpserver.listen(settings.localport, function () {
2054 - // We started listening.
2055 - if (settings.remotetarget == null) {
2056 - console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
2057 - } else {
2058 - console.log('Redirecting local port ' + settings.localport + ' to ' + settings.remotetarget + ':' + settings.remoteport + '.');
2059 - }
2060 - console.log("Press ctrl-c to exit.");
2113 + try {
2114 + tcpserver.listen(settings.localport, function () {
2115 + // We started listening.
2116 + if (settings.remotetarget == null) {
2117 + console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
2118 + } else {
2119 + console.log('Redirecting local port ' + settings.localport + ' to ' + settings.remotetarget + ':' + settings.remoteport + '.');
2120 + }
2121 + console.log("Press ctrl-c to exit.");
2122
2062 - // If settings has a "cmd", run it now.
2063 - //process.exec("notepad.exe");
2064 - });
2123 + // If settings has a "cmd", run it now.
2124 + //process.exec("notepad.exe");
2125 + });
2126 + } catch (ex) { console.log("Unable to bind to local TCP port " + settings.localport + "."); exit(1); return; }
2127 }
2128
2129 // Called when a TCP connect is received on the local port. Launch a tunnel.
@@ -2071,8 +2133,9 @@ function OnTcpClientConnected(c) {
2133 debug(1, "Client connected");
2134 c.on('end', function () { disconnectTunnel(this, this.websocket, "Client closed"); });
2135 c.pause();
2136 + var options;
2137 try {
2075 - options = http.parseUri(settings.serverurl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remotenodeid + '&tcpport=' + settings.remoteport + (settings.remotetarget == null ? '' : '&tcpaddr=' + settings.remotetarget));
2138 + options = http.parseUri(settings.serverurl + '?auth=' + settings.acookie + '&nodeid=' + settings.remotenodeid + '&tcpport=' + settings.remoteport + (settings.remotetarget == null ? '' : '&tcpaddr=' + settings.remotetarget));
2139 } catch (e) { console.log("Unable to parse \"serverUrl\"."); process.exit(1); return; }
2140 options.checkServerIdentity = onVerifyServer;
2141 options.rejectUnauthorized = false;
@@ -2106,8 +2169,8 @@ function OnWebSocket(msg, s, head) {
2169 }
2170 }
2171 });
2109 - s.on('error', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket error'); });
2110 - s.on('close', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
2172 + s.on('error', function () { disconnectTunnel(this.tcp, this, 'Websocket error'); });
2173 + s.on('close', function () { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
2174 s.parent = this;
2175 }
2176
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.5.1-b",
3 + "version": "0.5.1-c",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
webserver.js
+3 -3
@@ -3352,9 +3352,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3352 localPort: 1234,
3353 remoteName: node.name,
3354 remoteNodeId: node._id,
3355 - remoteTarget: '',
3355 + remoteTarget: null,
3356 remotePort: 3389,
3357 - username: '',
3357 + username: user.name,
3358 password: '',
3359 serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
3360 serverHttpsHash: Buffer.from(obj.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
@@ -3368,7 +3368,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3368 });
3369 } else if (req.query.meshaction == 'generic') {
3370 var meshaction = {
3371 - username: '',
3371 + username: user.name,
3372 password: '',
3373 serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
3374 serverHttpsHash: Buffer.from(obj.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate