diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe index 203f97c9..9890996f 100644 Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe index 68b374af..76c0cf80 100644 Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ diff --git a/agents/MeshService-signed.exe b/agents/MeshService-signed.exe index f7d4cc02..60b35c31 100644 Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ diff --git a/agents/MeshService.exe b/agents/MeshService.exe index 2df24540..81ac047d 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64-signed.exe b/agents/MeshService64-signed.exe index 88fcb2e8..d4f6f895 100644 Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 413af407..3d87e0a5 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/package.json b/package.json index e76f0901..d801fefc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.1.7-r", + "version": "0.1.7-s", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/images/mainwelcome-90.png b/public/images/mainwelcome-90.png new file mode 100644 index 00000000..ae287b0a Binary files /dev/null and b/public/images/mainwelcome-90.png differ diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars index 279ad480..cf679b30 100644 --- a/views/login-mobile.handlebars +++ b/views/login-mobile.handlebars @@ -29,23 +29,19 @@
Connect to your devices anywhere using MeshCentral, the remote monitoring and management web site. Get started by creating an account if you don't have one already.
-
-
+ ' + EscapeHtml(userinfo.email) + '.' , nolog: 1 }) + obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified: ' + EscapeHtml(userinfo.email) + '.', nolog: 1 }) } }); } @@ -538,7 +538,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate 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]; if (!user) return; - + // Check if the password is correct obj.authenticate(user.name, req.body.apassword1, domain, function (err, userid) { var user = obj.users[userid]; @@ -571,14 +571,14 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } }); } - + // Handle password changes function handlePasswordChangeRequest(req, res) { 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; } - + // Update the password require('./pass').hash(req.body.apassword1, function (err, salt, hash) { if (err) throw err; @@ -708,7 +708,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate if (obj.args.webrtc == true) { features += 128; } // Enable WebRTC (Default false for now) if (obj.args.clickonce !== false) { features += 256; } // Enable ClickOnce (Default true) if (obj.args.allowhighqualitydesktop == true) { features += 512; } // Enable AllowHighQualityDesktop (Default false) - + // Send the master web application if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' Logout'; } // If a default user is in use or no user mode, don't display the logout button var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified @@ -719,10 +719,10 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page. if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified - res.render(obj.path.join(__dirname, isModuleBrowser(req) ?'views/login-mobile':'views/login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer }); + res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/login-mobile' : 'views/login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer }); } } - + // Get the link to the root certificate if needed function getRootCertLink() { // TODO: This is not quite right, we need to check if the HTTPS certificate is issued from MeshCentralRoot, if so, add this download link. @@ -738,9 +738,9 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate 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 var user = obj.users[req.session.userid]; - res.render(obj.path.join(__dirname, isModuleBrowser(req)?'views/terms-mobile':'views/terms'), { title: domain.title, title2: domain.title2, logoutControl: 'Welcome ' + user.name + '. Logout' }); + res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2, logoutControl: 'Welcome ' + user.name + '. Logout' }); } else { - res.render(obj.path.join(__dirname, isModuleBrowser(req)?'views/terms-mobile':'views/terms'), { title: domain.title, title2: domain.title2 }); + res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2 }); } } @@ -771,7 +771,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // Figure out the MPS port, use the alias if set var mpsport = ((obj.args.mpsaliasport != null) ? obj.args.mpsaliasport : obj.args.mpsport); - + if ((serverNameSplit.length == 4) && (parseInt(serverNameSplit[0]) == serverNameSplit[0]) && (parseInt(serverNameSplit[1]) == serverNameSplit[1]) && (parseInt(serverNameSplit[2]) == serverNameSplit[2]) && (parseInt(serverNameSplit[3]) == serverNameSplit[3])) { // Server name is an IPv4 address var filepath = obj.parent.path.join(__dirname, 'public/scripts/cira_setup_script_ip.mescript'); @@ -837,7 +837,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate }); } } - + // Handle user public file downloads function handleDownloadUserFiles(req, res) { var domain = checkUserIpAddress(req, res); @@ -847,7 +847,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate if (domain.id != '') { domainname = 'domain-' + domain.id; } var path = obj.path.join(obj.filespath, domainname + "/user-" + spliturl[2] + "/Public"); for (var i = 3; i < spliturl.length; i++) { if (obj.common.IsFilenameValid(spliturl[i]) == true) { path += '/' + spliturl[i]; filename = spliturl[i]; } else { res.sendStatus(404); return; } } - + var stat = null; try { stat = obj.fs.statSync(path) } catch (e) { } if ((stat != null) && ((stat.mode & 0x004000) == 0)) { @@ -855,7 +855,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate 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 + '\"' }); try { res.sendFile(obj.path.resolve(__dirname, path)); } catch (e) { res.sendStatus(404); } } else { - res.render(obj.path.join(__dirname, 'views/download'), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, message: "" + filename + ", " + stat.size + " byte" + ((stat.size < 2)?'':'s') + "." }); + res.render(obj.path.join(__dirname, 'views/download'), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, message: "" + filename + ", " + stat.size + " byte" + ((stat.size < 2) ? '' : 's') + "." }); } } else { res.render(obj.path.join(__dirname, 'views/download'), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, message: "Invalid file link, please check the URL again." }); @@ -863,7 +863,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } // Take a "user/domain/userid/path/file" format and return the actual server disk file path if access is allowed - obj.getServerFilePath = function(user, domain, path) { + obj.getServerFilePath = function (user, domain, path) { var splitpath = path.split('/'), serverpath = obj.path.join(obj.filespath, 'domain'), filename = ''; if ((splitpath.length < 3) || (splitpath[0] != 'user' && splitpath[0] != 'mesh') || (splitpath[1] != domain.id)) return null; // Basic validation var objid = splitpath[0] + '/' + splitpath[1] + '/' + splitpath[2]; @@ -877,7 +877,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } // Return the maximum number of bytes allowed in the user account "My Files". - obj.getQuota = function(objid, domain) { + obj.getQuota = function (objid, domain) { if (objid == null) return 0; if (objid.startsWith('user/')) { var user = obj.users[objid]; @@ -907,7 +907,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=\"' + file.name + '\"' }); try { res.sendFile(file.fullpath); } catch (e) { res.sendStatus(404); } } - + // Upload a MeshCore.js file to the server function handleUploadMeshCoreFile(req, res) { var domain = checkUserIpAddress(req, res); @@ -915,7 +915,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate 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) - + var multiparty = require('multiparty'); var form = new multiparty.Form(); form.parse(req, function (err, fields, files) { @@ -987,7 +987,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate res.send(''); }); } - + // Subscribe to all events we are allowed to receive obj.subscribe = function (userid, target) { var user = obj.users[userid]; @@ -1035,11 +1035,11 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate if (docs.length == 0) { console.log('ERR: Node not found'); return; } var node = docs[0]; if (!node.intelamt) { console.log('ERR: Not AMT node'); return; } - + // Check if this user has permission to manage this computer var meshlinks = user.links[node.meshid]; if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); return; } - + // Check what connectivity is available for this node var state = parent.GetConnectivityState(req.query.host); var conn = 0; @@ -1075,7 +1075,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate //if (node.intelamt.tls == 0) port = 16992; // DEBUG: Allow TLS flag to set TLS mode within CIRA if (ciraconn.tag.boundPorts.indexOf(16992) >= 0) port = 16992; // RELEASE: Always use non-TLS mode if available within CIRA if (req.query.p == 2) port += 2; - + // 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 ) @@ -1108,7 +1108,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate var tlsock = new TLSSocket(ser, tlsoptions); tlsock.on('error', function (err) { Debug(1, "CIRA TLS Connection Error ", err); }); tlsock.on('secureConnect', function () { Debug(2, "CIRA Secure TLS Connection"); ws.resume(); }); - + // Decrypted tunnel from TLS communcation to be forwarded to websocket tlsock.on('data', function (data) { // AMT/TLS ---> WS @@ -1128,7 +1128,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate ws.forwardclient.xtls = 0; ws.resume(); } - + // When data is received from the web socket, forward the data into the associated CIRA cahnnel. // If the CIRA connection is pending, the CIRA channel has built-in buffering, so we are ok sending anyway. ws.on('message', function (msg) { @@ -1146,23 +1146,23 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate Debug(1, 'Websocket relay closed.'); if (ws.forwardclient && ws.forwardclient.close) { ws.forwardclient.close(); } // TODO: If TLS is used, we need to close the socket that is wrapped by TLS }); - + ws.forwardclient.onStateChange = function (ciraconn, state) { Debug(2, 'Relay CIRA state change', state); if (state == 0) { try { ws.close(); } catch (e) { } } } - + ws.forwardclient.onData = function (ciraconn, data) { Debug(4, 'Relay CIRA data', data.length); if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor if (data.length > 0) { try { ws.send(data); } catch (e) { } } // TODO: Add TLS support } - + ws.forwardclient.onSendOk = function (ciraconn) { // TODO: Flow control? (Dont' really need it with AMT, but would be nice) //console.log('onSendOk'); } - + // Fetch Intel AMT credentials & Setup interceptor if (req.query.p == 1) { Debug(3, 'INTERCEPTOR1', { host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass }); @@ -1177,7 +1177,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate return; } - + // 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 + '.'); @@ -1203,7 +1203,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate var port = 16992; if (node.intelamt.tls > 0) port = 16993; // This is a direct connection, use TLS when possible if (req.query.p == 2) port += 2; - + if (node.intelamt.tls == 0) { // If this is TCP (without TLS) set a normal TCP socket ws.forwardclient = new obj.net.Socket(); @@ -1224,30 +1224,30 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate ws.forwardclient.xstate = 0; ws.forwardclient.forwardwsocket = ws; } - + // When we receive data on the TCP connection, forward it back into the web socket connection. ws.forwardclient.on('data', function (data) { Debug(1, 'TCP relay data from ' + node.host + ', ' + data.length + ' bytes.'); // DEBUG if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor try { ws.send(new Buffer(data, 'binary')); } catch (e) { } }); - + // If the TCP connection closes, disconnect the associated web socket. ws.forwardclient.on('close', function () { Debug(1, 'TCP relay disconnected from ' + node.host + '.'); try { ws.close(); } catch (e) { } }); - + // If the TCP connection causes an error, disconnect the associated web socket. ws.forwardclient.on('error', function (err) { Debug(1, 'TCP relay error from ' + node.host + ': ' + err.errno); try { ws.close(); } catch (e) { } }); - + // Fetch Intel AMT credentials & Setup interceptor if (req.query.p == 1) { ws.interceptor = obj.interceptor.CreateHttpInterceptor({ host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass }); } else if (req.query.p == 2) { ws.interceptor = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass }); } - + if (node.intelamt.tls == 0) { // A TCP connection to Intel AMT just connected, start forwarding. ws.forwardclient.connect(port, node.host, function () { @@ -1284,7 +1284,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate // If closed, do nothing ws.on('close', function (req) { }); } - + // Get the total size of all files in a folder and all sub-folders. (TODO: try to make all async version) function readTotalFileSize(path) { var r = 0, dir; @@ -1295,7 +1295,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate } return r; } - + // Delete a folder and all sub items. (TODO: try to make all async version) function deleteFolderRec(path) { if (obj.fs.existsSync(path) == false) return; @@ -1305,7 +1305,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate }); obj.fs.rmdirSync(path); }; - + // Handle Intel AMT events // To subscribe, add "http://server:port/amtevents.ashx" to Intel AMT subscriptions. obj.handleAmtEventRequest = function (req, res) { @@ -1316,29 +1316,29 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate if (authstr.substring(0, 7) == "Digest ") { var auth = obj.common.parseNameValueList(obj.common.quoteSplit(authstr.substring(7))); if ((req.url === auth.uri) && (obj.httpAuthRealm === auth.realm) && (auth.opaque === obj.crypto.createHmac('SHA384', obj.httpAuthRandom).update(auth.nonce).digest('hex'))) { - + // Read the data, we need to get the arg field var eventData = ''; req.on('data', function (chunk) { eventData += chunk; }); req.on('end', function () { - + // Completed event read, let get the argument that must contain the nodeid var i = eventData.indexOf(' |