From 5474f1d3869cd6dbe6e0d1a22c5637a8792651c9 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 2 Oct 2017 14:12:29 -0700 Subject: [PATCH] Improved web interface for getting started. --- MeshCentralServer.njsproj | 3 -- agents/meshcore.js | 4 +- certoperations.js | 65 +++++++++++++---------- meshcentral.js | 10 ++-- mesherrors.txt | 94 +++++++++++++++++++++++++++++++++ meshscanner.js | 68 +++++++++++++----------- package.json | 2 +- views/default.handlebars | 38 ++++++++------ webserver.js | 106 ++++++++++++++++++++++---------------- 9 files changed, 263 insertions(+), 127 deletions(-) diff --git a/MeshCentralServer.njsproj b/MeshCentralServer.njsproj index 91e8d00e..1b2aca30 100644 --- a/MeshCentralServer.njsproj +++ b/MeshCentralServer.njsproj @@ -71,7 +71,6 @@ - @@ -120,8 +119,6 @@ - - diff --git a/agents/meshcore.js b/agents/meshcore.js index 00bf98c6..53ce0ad2 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -277,9 +277,9 @@ function createMeshCore(agent) { else if (data.type == 'tunnel') { // Process a new tunnel connection request if (data.value && data.sessionid) { // Create a new tunnel object - //sendConsoleText(data.value); + sendConsoleText(data.value); var xurl = getServerTargetUrlEx(data.value); - //sendConsoleText(xurl); + sendConsoleText(xurl); if (xurl != null) { var tunnel = http.request(http.parseUri(xurl)); tunnel.upgrade = onTunnelUpgrade; diff --git a/certoperations.js b/certoperations.js index 1d332281..e9e0fe04 100644 --- a/certoperations.js +++ b/certoperations.js @@ -38,24 +38,23 @@ module.exports.CertificateOperations = function () { cert.validity.notAfter = new Date(); cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30); if (addThumbPrintToName == true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); } - var attrs = [ { name: 'commonName', value: commonName } ]; - if (country != undefined) attrs.push({ name: 'countryName', value: country }); - if (organization != undefined) attrs.push({ name: 'organizationName', value: organization }); + if (country == undefined) { country = 'unknown'; } + if (organization == undefined) { organization = 'unknown'; } + var attrs = [{ name: 'commonName', value: commonName }, { name: 'organizationName', value: organization }, { name: 'countryName', value: country }]; cert.setSubject(attrs); cert.setIssuer(attrs); - cert.setExtensions([ - { name: 'basicConstraints', cA: true }, - { + // Create a root certificate + cert.setExtensions([{ + name: 'basicConstraints', + cA: true + }, { name: 'nsCertType', - client: false, - server: false, - email: false, - objsign: false, sslCA: true, - emailCA: false, + emailCA: true, objCA: true - } - ]); + }, { + name: 'subjectKeyIdentifier' + }]); cert.sign(keys.privateKey, obj.forge.md.sha256.create()); return { cert: cert, key: keys.privateKey }; @@ -136,7 +135,8 @@ module.exports.CertificateOperations = function () { } // Returns the web server TLS certificate and private key, if not present, create demonstration ones. - obj.GetMeshServerCertificate = function (directory, certargs, func) { + obj.GetMeshServerCertificate = function (directory, args, func) { + var certargs = args.cert; // commonName, country, organization // If the certificates directory does not exist, create it. @@ -151,16 +151,25 @@ module.exports.CertificateOperations = function () { r.root = { cert: rootCertificate, key: rootPrivateKey }; rcount++; } - - // If the web certificate already exist, load it - if (obj.fileExists(directory + '/webserver-cert-public.crt') && obj.fileExists(directory + '/webserver-cert-private.key')) { - var webCertificate = obj.fs.readFileSync(directory + '/webserver-cert-public.crt', 'utf8'); - var webPrivateKey = obj.fs.readFileSync(directory + '/webserver-cert-private.key', 'utf8'); - r.web = { cert: webCertificate, key: webPrivateKey }; - rcount++; + + if (args.tlsoffload == true) { + // If the web certificate already exist, load it. Load just the certificate since we are in TLS offload situation + if (obj.fileExists(directory + '/webserver-cert-public.crt')) { + var webCertificate = obj.fs.readFileSync(directory + '/webserver-cert-public.crt', 'utf8'); + r.web = { cert: webCertificate }; + rcount++; + } + } else { + // If the web certificate already exist, load it. Load both certificate and private key + if (obj.fileExists(directory + '/webserver-cert-public.crt') && obj.fileExists(directory + '/webserver-cert-private.key')) { + var webCertificate = obj.fs.readFileSync(directory + '/webserver-cert-public.crt', 'utf8'); + var webPrivateKey = obj.fs.readFileSync(directory + '/webserver-cert-private.key', 'utf8'); + r.web = { cert: webCertificate, key: webPrivateKey }; + rcount++; + } } - // If the bin certificate already exist, load it + // If the mps certificate already exist, load it if (obj.fileExists(directory + '/mpsserver-cert-public.crt') && obj.fileExists(directory + '/mpsserver-cert-private.key')) { var mpsCertificate = obj.fs.readFileSync(directory + '/mpsserver-cert-public.crt', 'utf8'); var mpsPrivateKey = obj.fs.readFileSync(directory + '/mpsserver-cert-private.key', 'utf8'); @@ -168,7 +177,7 @@ module.exports.CertificateOperations = function () { rcount++; } - // If the bin certificate already exist, load it + // If the agent certificate already exist, load it if (obj.fileExists(directory + '/agentserver-cert-public.crt') && obj.fileExists(directory + '/agentserver-cert-private.key')) { var agentCertificate = obj.fs.readFileSync(directory + '/agentserver-cert-public.crt', 'utf8'); var agentPrivateKey = obj.fs.readFileSync(directory + '/agentserver-cert-private.key', 'utf8'); @@ -176,7 +185,7 @@ module.exports.CertificateOperations = function () { rcount++; } - // If the bin certificate already exist, load it + // If the console certificate already exist, load it if (obj.fileExists(directory + '/amtconsole-cert-public.crt') && obj.fileExists(directory + '/agentserver-cert-private.key')) { var amtConsoleCertificate = obj.fs.readFileSync(directory + '/amtconsole-cert-public.crt', 'utf8'); var amtConsolePrivateKey = obj.fs.readFileSync(directory + '/amtconsole-cert-private.key', 'utf8'); @@ -198,14 +207,14 @@ module.exports.CertificateOperations = function () { r.calist = calist; // Decode certificate arguments - var commonName = 'un-configured', country, organization; + var commonName = 'un-configured', country, organization, forceWebCertGen = 0; if (certargs != undefined) { var args = certargs.split(','); if (args.length > 0) commonName = args[0]; if (args.length > 1) country = args[1]; if (args.length > 2) organization = args[2]; } - + if (rcount == 5) { // Fetch the Intel AMT console name var consoleCertificate = obj.pki.certificateFromPem(r.console.cert); @@ -220,7 +229,7 @@ module.exports.CertificateOperations = function () { if (xcountryField != null) { xcountry = xcountryField.value; } var xorganization, xorganizationField = webCertificate.subject.getField('O'); if (xorganizationField != null) { xorganization = xorganizationField.value; } - if ((r.CommonName == commonName) && (xcountry == country) && (xorganization == organization)) { if (func != undefined) { func(r); } return r; } // If the certificate matches what we want, keep it. + if ((r.CommonName == commonName) && (xcountry == country) && (xorganization == organization)) { if (func != undefined) { func(r); } return r; } else { forceWebCertGen = 1; } // If the certificate matches what we want, keep it. } console.log('Generating certificates...'); @@ -242,7 +251,7 @@ module.exports.CertificateOperations = function () { // If the web certificate does not exist, create one var webCertAndKey, webCertificate, webPrivateKey; - if (r.web == undefined) { + if ((r.web == undefined) || (forceWebCertGen == 1)) { webCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization); webCertificate = obj.pki.certificateToPem(webCertAndKey.cert); webPrivateKey = obj.pki.privateKeyToPem(webCertAndKey.key); diff --git a/meshcentral.js b/meshcentral.js index d76831b3..85ef0006 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -201,8 +201,12 @@ function CreateMeshCentralServer() { var xdomains = {}; for (var i in obj.config.domains) { if (!obj.config.domains[i].title) { obj.config.domains[i].title = 'MeshCentral'; } if (!obj.config.domains[i].title2) { obj.config.domains[i].title2 = '2.0 Beta 1'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains; var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains for (var i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } } - for (var i in obj.config.domains) { obj.config.domains[i].url = (i == '')?'/':('/' + i + '/'); obj.config.domains[i].id = i; } - + for (var i in obj.config.domains) { + for (var j in obj.config.domains[i]) { obj.config.domains[i][j.toLocaleLowerCase()] = obj.config.domains[i][j]; } // LowerCase all domain keys + obj.config.domains[i].url = (i == '') ? '/' : ('/' + i + '/'); obj.config.domains[i].id = i; + if (typeof obj.config.domains[i].userallowedip == 'string') { obj.config.domains[i].userallowedip = null; if (obj.config.domains[i].userallowedip != "") { obj.config.domains[i].userallowedip = obj.config.domains[i].userallowedip.split(','); } } + } + // Log passed arguments into Windows Service Log //if (obj.servicelog != null) { var s = ''; for (var i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); } @@ -283,7 +287,7 @@ function CreateMeshCentralServer() { obj.updateMeshCore(); // Load server certificates - obj.certificateOperations.GetMeshServerCertificate(obj.datapath, obj.args.cert, function (certs) { + obj.certificateOperations.GetMeshServerCertificate(obj.datapath, obj.args, function (certs) { obj.certificates = certs; // If the certificate is un-configured, force LAN-only mode diff --git a/mesherrors.txt b/mesherrors.txt index 8ef180d8..8f9fad29 100644 --- a/mesherrors.txt +++ b/mesherrors.txt @@ -189,3 +189,97 @@ TypeError: Cannot read property 'SSL_OP_NO_SSLv2' of undefined at Immediate._onImmediate (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\async\lib\async.js:498:34) +-------- 10/1/2017, 10:21:38 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:91 + console.log(obj.servers4.keys()); + ^ + +TypeError: obj.servers4.keys is not a function + at setupServers (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:91:34) + at Object.obj.start (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:136:9) + at InternalFieldObject.ondone (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:328:98) + + +-------- 10/1/2017, 10:21:40 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:91 + console.log(obj.servers4.keys()); + ^ + +TypeError: obj.servers4.keys is not a function + at setupServers (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:91:34) + at Object.obj.start (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:136:9) + at InternalFieldObject.ondone (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:328:98) + + +-------- 10/1/2017, 10:54:43 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:96 + performScan(server6); + ^ + +ReferenceError: performScan is not defined + at Socket. (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:96:21) + at Socket.g (events.js:286:16) + at emitNone (events.js:86:13) + at Socket.emit (events.js:185:7) + at startListening (dgram.js:121:10) + at dgram.js:228:7 + at _combinedTickCallback (internal/process/next_tick.js:77:11) + at process._tickCallback (internal/process/next_tick.js:98:9) + + +-------- 10/1/2017, 10:54:45 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:96 + performScan(server6); + ^ + +ReferenceError: performScan is not defined + at Socket. (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:96:21) + at Socket.g (events.js:286:16) + at emitNone (events.js:86:13) + at Socket.emit (events.js:185:7) + at startListening (dgram.js:121:10) + at dgram.js:228:7 + at _combinedTickCallback (internal/process/next_tick.js:77:11) + at process._tickCallback (internal/process/next_tick.js:98:9) + + +-------- 10/1/2017, 10:54:47 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:96 + performScan(server6); + ^ + +ReferenceError: performScan is not defined + at Socket. (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshscanner.js:96:21) + at Socket.g (events.js:286:16) + at emitNone (events.js:86:13) + at Socket.emit (events.js:185:7) + at startListening (dgram.js:121:10) + at dgram.js:228:7 + at _combinedTickCallback (internal/process/next_tick.js:77:11) + at process._tickCallback (internal/process/next_tick.js:98:9) + + +-------- 10/1/2017, 11:49:12 AM -------- + +C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\node-forge\js\x509.js:2052 + throw error; + ^ + +Error: Attribute value not specified. + at _fillMissingFields (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\node-forge\js\x509.js:2050:19) + at Object.cert.setSubject (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\node-forge\js\x509.js:948:5) + at Object.obj.GenerateRootCertificate (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\certoperations.js:44:14) + at Object.obj.GetMeshServerCertificate (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\certoperations.js:230:34) + at C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\meshcentral.js:286:43 + at newArguments.(anonymous function) (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\nedb\lib\executor.js:29:17) + at Cursor.execFn (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\nedb\lib\datastore.js:484:12) + at callback (C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\nedb\lib\cursor.js:126:19) + at C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\nedb\lib\cursor.js:193:12 + at C:\Users\Default.DESKTOP-M9I88C9\Desktop\AmtWebApp\meshcentral\node_modules\nedb\lib\datastore.js:329:14 + + diff --git a/meshscanner.js b/meshscanner.js index 7e990d07..7f95e9b4 100644 --- a/meshscanner.js +++ b/meshscanner.js @@ -22,8 +22,7 @@ module.exports.CreateMeshScanner = function (parent) { // Get a list of IPv4 and IPv6 interface addresses function getInterfaceList() { - var ipv4 = []; - var ipv6 = []; + var ipv4 = ['*'], ipv6 = ['*']; var interfaces = require('os').networkInterfaces(); for (var i in interfaces) { var interface = interfaces[i]; @@ -50,25 +49,32 @@ module.exports.CreateMeshScanner = function (parent) { obj.servers4[localAddress].xxclear = false; } else { // Create a new IPv4 server - var server4 = obj.dgram.createSocket("udp4"); - server4.xxclear = false; - server4.xxtype = 4; - server4.xxlocal = localAddress; - server4.on('error', function (err) { console.log("ERROR: Server port " + server4.xxlocal + ":16989 not available, check if server is running twice."); server4.close(); server4 = null; }); - server4.bind({ address: server4.xxlocal, port: 16989, exclusive: false }, function () { - try { - server4.setBroadcast(true); - server4.setMulticastTTL(128); - server4.addMembership(membershipIPv4); - server4.on('error', function (error) { console.log('Error: ' + error); }); - server4.on('message', function (msg, info) { onUdpPacket(msg, info, server4); }); - obj.performScan(server4); - obj.performScan(server4); - } catch (e) { } - }); - obj.servers4[localAddress] = server4; + try { + var server4 = obj.dgram.createSocket("udp4"); + server4.xxclear = false; + server4.xxtype = 4; + server4.xxlocal = localAddress; + server4.on('error', function (err) { console.log("ERROR: Server port " + server4.xxlocal + ":16989 not available, check if server is running twice."); server4.close(); server4 = null; }); + var bindOptions = { port: 16989, exclusive: false }; + if (server4.xxlocal != '*') { bindOptions.address = server4.xxlocal; } + server4.bind(bindOptions, function () { + try { + this.setBroadcast(true); + this.setMulticastTTL(128); + this.addMembership(membershipIPv4); + server4.on('error', function (error) { console.log('Error: ' + error); }); + server4.on('message', function (msg, info) { onUdpPacket(msg, info, server4); }); + obj.performScan(this); + obj.performScan(this); + } catch (e) { } + }); + obj.servers4[localAddress] = server4; + } catch (e) { + console.log(e); + } } } + for (var i in addresses.ipv6) { var localAddress = addresses.ipv6[i]; if (obj.servers6[localAddress] != null) { @@ -80,16 +86,18 @@ module.exports.CreateMeshScanner = function (parent) { server6.xxclear = false; server6.xxtype = 6; server6.xxlocal = localAddress; - server6.on('error', function (err) { console.log("ERROR: Server port [" + server6.xxlocal + "]:16989 not available, check if server is running twice.");server6.close(); obj.server6 = null; }); - server6.bind({ address: server6.xxlocal, port: 16989, exclusive: false }, function () { + server6.on('error', function (err) { console.log("ERROR: Server port [" + server6.xxlocal + "]:16989 not available, check if server is running twice."); server6.close(); obj.server6 = null; }); + var bindOptions = { port: 16989, exclusive: false }; + if (server6.xxlocal != '*') { bindOptions.address = server6.xxlocal; } + server6.bind(bindOptions, function () { try { - server6.setBroadcast(true); - server6.setMulticastTTL(128); - server6.addMembership(membershipIPv6); - server6.on('error', function (error) { console.log('Error: ' + error); }); - server6.on('message', function (msg, info) { onUdpPacket(msg, info, server6); }); - performScan(server6); - performScan(server6); + this.setBroadcast(true); + this.setMulticastTTL(128); + this.addMembership(membershipIPv6); + this.on('error', function (error) { console.log('Error: ' + error); }); + this.on('message', function (msg, info) { onUdpPacket(msg, info, this); }); + obj.performScan(this); + obj.performScan(this); } catch (e) { } }); obj.servers6[localAddress] = server6; @@ -128,6 +136,8 @@ module.exports.CreateMeshScanner = function (parent) { if (server != null) { if (server.xxtype == 4) { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } if (server.xxtype == 6) { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } + if ((server.xxtype == 4) && (server.xxlocal == '*')) { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '127.0.0.1'); server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '255.255.255.255'); } + if ((server.xxtype == 6) && (server.xxlocal == '*')) { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, '::1'); } } else { for (var i in obj.servers4) { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } for (var i in obj.servers6) { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } @@ -137,7 +147,7 @@ module.exports.CreateMeshScanner = function (parent) { // Called when a UDP packet is received from an agent. function onUdpPacket(msg, info, server) { - // console.log('Received ' + msg.length + ' bytes from ' + info.address + ':' + info.port + ', on interface: ' + server.xxlocal + '.'); + //console.log('Received ' + msg.length + ' bytes from ' + info.address + ':' + info.port + ', on interface: ' + server.xxlocal + '.'); if ((msg.length == 64) && (msg.toString('ascii') == obj.agentCertificatHashHex.toUpperCase())) { if (server.xxtype == 4) { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, info.port, info.address); } if (server.xxtype == 6) { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, info.port, info.address); } diff --git a/package.json b/package.json index dd1386f7..1f03699a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.0.8-g", + "version": "0.0.8-j", "keywords": [ "Remote Management", "Intel AMT", diff --git a/views/default.handlebars b/views/default.handlebars index 2dd7cee9..7e74e6d9 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -119,7 +119,7 @@
 
-
+
View   -   -   -   -   -   -   +   +   +   +   +   +   +  
@@ -602,6 +601,7 @@ var showHostnames = false; var meshserver = null; var meshes = {}; + var meshcount = 0; var nodes = []; var filetree = {}; var userinfo = null; @@ -1195,8 +1195,11 @@ QV('devListToolbar', view < 3); QV('devMapToolbar', view == 3); QV('devListToolbarSort', view < 3); + QV('NoMeshesPanel', meshcount == 0); + QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0)); + QV('devListToolbarSort', (meshcount != 0) && (nodes.length > 0)); + if ((meshcount == 0) || (nodes.length == 0)) { view = 1; } if (view == 3) { - QV('NoNodesPanel',false); //Hide NoNodesPanel user views Map setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200); // TODO } else { @@ -1300,9 +1303,12 @@ } r += '
'; // This height of 1 div fixes a problem in Linux firefox browsers + // Add a "Add Mesh" option + if (view == 1) { r += ''; } + QH('xdevices', r); deviceHeaderSet(); - QV('NoNodesPanel', count == 0); + //QV('NoMeshesPanel', count == 0); // Re-check nodeid's var elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0; @@ -1671,7 +1677,6 @@ markersLayer: null, mapLayer: null, // Create a tile and use OSM source mapView: null, // Sets the initial view - selectedNodes: {} // Save details of Node that was selected from 'Put a node' option from contextmenu } // Add a feature for every Node and change style if connection status changes @@ -3642,6 +3647,7 @@ } r += ''; + meshcount = count; QH('p2meshes', r); QV('p2noMeshFound', count == 0); } diff --git a/webserver.js b/webserver.js index 168e846e..0e973d5f 100644 --- a/webserver.js +++ b/webserver.js @@ -138,7 +138,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate for (var i in docs) { var u = obj.users[docs[i]._id] = docs[i]; domainUserCount[u.domain]++; } for (var i in parent.config.domains) { if (domainUserCount[i] == 0) { - if (parent.config.domains[i].newAccounts == 0) { parent.config.domains[i].newAccounts = 2; } + if (parent.config.domains[i].newaccounts == 0) { parent.config.domains[i].newaccounts = 2; } console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.'); } } @@ -178,22 +178,35 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } */ - // Check if the source IP address is allowed, return false if not - function checkUserIpAddress(req, res) { - if (obj.userAllowedIp == null) { return true; } + // Check if the source IP address is allowed for a given allowed list, return false if not + function checkUserIpAddressEx(req, res, allowedIpList) { + if (allowedIpList == null) { return true; } try { - if (typeof obj.userAllowedIp == 'string') { if (obj.userAllowedIp == "") { obj.userAllowedIp = null; return true; } else { obj.userAllowedIp = obj.userAllowedIp.split(','); } } var ip = null, type = 0; if (req.connection) { ip = req.connection.remoteAddress; type = 1; } // HTTP(S) request else if (req._socket) { ip = req._socket.remoteAddress; type = 2; } // WebSocket request if (ip.startsWith('::ffff:')) { ip = ip.substring(7); } // Fix IPv4 IP's encoded in IPv6 form - if ((ip != null) && (obj.userAllowedIp.indexOf(ip) >= 0)) { return true; } + if ((ip != null) && (allowedIpList.indexOf(ip) >= 0)) { return true; } if (type == 1) { res.sendStatus(401); } else if (type == 2) { try { req.close(); } catch (e) { } } } catch (e) { console.log(e); } return false; } + // Check if the source IP address is allowed, return domain if allowed + function checkUserIpAddress(req, res, rootonly) { + if (obj.userAllowedIp != null) { + if (typeof obj.userAllowedIp == 'string') { if (obj.userAllowedIp == "") { obj.userAllowedIp = null; return true; } else { obj.userAllowedIp = obj.userAllowedIp.split(','); } } + if (checkUserIpAddressEx(req, res, obj.userAllowedIp) == false) return null; + } + if (rootonly == true) return; + var domain; + if (req.url) { domain = getDomain(req); } else { domain = getDomain(res); } + if (domain.userallowedip == null) return domain; + if (checkUserIpAddressEx(req, res, domain.userallowedip) == false) return null; + return domain; + } + // Return the current domain of the request function getDomain(req) { var x = req.url.split('/'); @@ -203,8 +216,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleLogoutRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); // Destroy the user's session to log them out will be re-created next request if (req.session.userid) { @@ -217,8 +230,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleLoginRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; obj.authenticate(req.body.username, req.body.password, domain, function (err, userid, passhint) { if (userid) { var user = obj.users[userid]; @@ -269,16 +282,16 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleCreateAccountRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); - if (domain.newAccounts == 0) { res.sendStatus(401); return; } + var domain = checkUserIpAddress(req, res); + if (domain == null) return; + if (domain.newaccounts == 0) { res.sendStatus(401); return; } if (!req.body.username || !req.body.email || !req.body.password1 || !req.body.password2 || (req.body.password1 != req.body.password2) || req.body.username == '~') { req.session.loginmode = 2; req.session.error = 'Unable to create account.';; res.redirect(domain.url); } else { // Check if there is domain.newAccountToken, check if supplied token is valid - if ((domain.newAccountsPass != null) && (domain.newAccountsPass != '') && (req.body.anewaccountpass != domain.newAccountsPass)) { + if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) { req.session.loginmode = 2; req.session.error = 'Invalid account creation token.'; res.redirect(domain.url); @@ -294,7 +307,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate var user = { type: 'user', _id: 'user/' + domain.id + '/' + req.body.username.toLowerCase(), name: req.body.username, email: req.body.email, creation: Date.now(), login: Date.now(), domain: domain.id, passhint: hint }; var usercount = 0; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } - if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newAccounts == 2) { domain.newAccounts = 0; } } // If this is the first user, give the account site admin. + if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts == 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin. obj.users[user._id] = user; req.session.userid = user._id; req.session.domainid = domain.id; @@ -312,8 +325,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } function handleDeleteAccountRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; // Check if the user is logged and we have all required parameters if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; } var user = obj.users[req.session.userid]; @@ -335,8 +348,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle password changes function handlePasswordChangeRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; // Check if the user is logged and we have all required parameters if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; } @@ -359,7 +372,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Indicates that any request to "/" should render "default" or "login" depending on login state function handleRootRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } + var domain = checkUserIpAddress(req, res); + if (domain == null) return; if (!obj.args) { res.sendStatus(500); return; } var domain = getDomain(req); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); @@ -412,7 +426,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate res.render(obj.path.join(__dirname, 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.certificates.CommonName, serverPublicPort: args.port, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass }); } else { // Send back the login application - res.render(obj.path.join(__dirname, 'views/login'), { loginmode: req.session.loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newAccounts, newAccountPass: (((domain.newAccountsPass == null) || (domain.newAccountsPass == ''))?0:1), serverDnsName: obj.certificates.CommonName, serverPublicPort: obj.args.port }); + res.render(obj.path.join(__dirname, 'views/login'), { loginmode: req.session.loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == ''))?0:1), serverDnsName: obj.certificates.CommonName, serverPublicPort: obj.args.port }); } } @@ -425,8 +439,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Renter the terms of service. function handleTermsRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); if (req.session && req.session.userid) { if (req.session.domainid != domain.id) { req.session.destroy(function () { res.redirect(domain.url); }); return; } // Check is the session is for the correct domain @@ -449,14 +463,14 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Returns the mesh server root certificate function handleRootCertRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } + if (checkUserIpAddress(req, res, true) == false) { return; } res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + certificates.RootName + '.cer' }); res.send(new Buffer(getRootCertBase64(), 'base64')); } // Returns an mescript for Intel AMT configuration function handleMeScriptRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } + if (checkUserIpAddress(req, res, true) == false) { return; } if (req.query.type == 1) { var filename = 'cira_setup.mescript'; res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + filename }); @@ -527,8 +541,9 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle user public file downloads function handleDownloadUserFiles(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req), domainname = 'domain', spliturl = decodeURIComponent(req.path).split('/'), filename = ''; + var domain = checkUserIpAddress(req, res); + if (domain == null) return; + var domainname = 'domain', spliturl = decodeURIComponent(req.path).split('/'), filename = ''; if ((spliturl.length < 3) || (obj.common.IsFilenameValid(spliturl[2]) == false) || (domain.userQuota == -1)) { res.sendStatus(404); return; } if (domain.id != '') { domainname = 'domain-' + domain.id; } var path = obj.path.join(obj.filespath, domainname + "/user-" + spliturl[2] + "/Public"); @@ -550,8 +565,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Download a file from the server function handleDownloadFile(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; if ((req.query.link == null) || (req.session == null) || (req.session.userid == null) || (domain == null) || (domain.userQuota == -1)) { res.sendStatus(404); return; } var user = obj.users[req.session.userid]; if (user == null) { res.sendStatus(404); return; } @@ -563,8 +578,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Upload a MeshCore.js file to the server function handleUploadMeshCoreFile(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; if (user.siteadmin != 0xFFFFFFFF) { res.sendStatus(401); return; } // Check if we have mesh core upload rights (Full admin only) @@ -588,8 +603,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Upload a file to the server function handleUploadFile(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (domain.userQuota == -1)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; if ((user.siteadmin & 8) == 0) { res.sendStatus(401); return; } // Check if we have file rights @@ -650,8 +665,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a web socket relay request function handleRelayWebSocket(ws, req) { - if (checkUserIpAddress(ws, req) == false) { return; } - var node, domain = getDomain(req); + var domain = checkUserIpAddress(ws, req); + if (domain == null) return; // Check if this is a logged in user var user, peering = true; if (req.query.auth == null) { @@ -677,7 +692,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Fetch information about the target obj.db.Get(req.query.host, function (err, docs) { if (docs.length == 0) { console.log('ERR: Node not found'); return; } - node = docs[0]; + var node = docs[0]; if (!node.intelamt) { console.log('ERR: Not AMT node'); return; } // Check if this user has permission to manage this computer @@ -915,7 +930,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle the web socket echo request, just echo back the data sent function handleEchoWebSocket(ws, req) { - if (checkUserIpAddress(ws, req) == false) { return; } + var domain = checkUserIpAddress(ws, req); + if (domain == null) return; // When data is received from the web socket, echo it back ws.on('message', function (data) { @@ -968,8 +984,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Indicates we want to handle websocket requests on "/control.ashx". function handleControlRequest(ws, req) { - if (checkUserIpAddress(ws, req) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(ws, req); + if (domain == null) return; try { // Check if the user is logged in if ((!req.session) || (!req.session.userid) || (req.session.domainid != domain.id)) { try { ws.close(); } catch (e) { } return; } @@ -1840,8 +1856,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a server backup request function handleBackupRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (obj.parent.args.noserverbackup == 1)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; if ((user.siteadmin & 1) == 0) { res.sendStatus(401); return; } // Check if we have server backup rights @@ -1873,8 +1889,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a server restore request function handleRestoreRequest(req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid) || (obj.parent.args.noserverbackup == 1)) { res.sendStatus(401); return; } var user = obj.users[req.session.userid]; if ((user.siteadmin & 4) == 0) { res.sendStatus(401); return; } // Check if we have server restore rights @@ -1918,8 +1934,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Handle a request to download a mesh settings obj.handleMeshSettingsRequest = function (req, res) { - if (checkUserIpAddress(req, res) == false) { return; } - var domain = getDomain(req); + var domain = checkUserIpAddress(req, res); + if (domain == null) return; //if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; } // Delete a mesh and all computers within it