diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe
index e6ff70eb..694c8e37 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 1f041bfb..400486e7 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 0ffbb0e5..8985bb9f 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 88384044..fc808b22 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 c50a96f8..e68f08e3 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 bfb44701..f64dbb33 100644
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
diff --git a/agents/modules_meshcore/monitor-info.js b/agents/modules_meshcore/monitor-info.js
index 1795165d..fe4c7454 100644
--- a/agents/modules_meshcore/monitor-info.js
+++ b/agents/modules_meshcore/monitor-info.js
@@ -1,5 +1,5 @@
/*
-Copyright 2018-2019 Intel Corporation
+Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -193,11 +193,14 @@ function monitorinfo()
this._X11 = this._gm.CreateNativeProxy(this.Location_X11LIB);
this._X11.CreateMethod('XChangeProperty');
this._X11.CreateMethod('XCloseDisplay');
+ this._X11.CreateMethod('XConnectionNumber');
+ this._X11.CreateMethod('XConvertSelection');
this._X11.CreateMethod('XCreateGC');
this._X11.CreateMethod('XCreateWindow');
this._X11.CreateMethod('XCreateSimpleWindow');
this._X11.CreateMethod('XDefaultColormap');
this._X11.CreateMethod('XDefaultScreen');
+ this._X11.CreateMethod('XDestroyWindow');
this._X11.CreateMethod('XDrawLine');
this._X11.CreateMethod('XDisplayHeight');
this._X11.CreateMethod('XDisplayWidth');
@@ -209,8 +212,11 @@ function monitorinfo()
this._X11.CreateMethod('XInternAtom');
this._X11.CreateMethod('XMapWindow');
this._X11.CreateMethod({ method: 'XNextEvent', threadDispatch: true });
+ this._X11.CreateMethod({ method: 'XNextEvent', newName: 'XNextEventSync' });
this._X11.CreateMethod('XOpenDisplay');
+ this._X11.CreateMethod('XPending');
this._X11.CreateMethod('XRootWindow');
+ this._X11.CreateMethod('XSelectInput');
this._X11.CreateMethod('XScreenCount');
this._X11.CreateMethod('XScreenOfDisplay');
this._X11.CreateMethod('XSelectInput');
@@ -220,7 +226,7 @@ function monitorinfo()
this._X11.CreateMethod('XSetLineAttributes');
this._X11.CreateMethod('XSetNormalHints');
this._X11.CreateMethod('XSetSubwindowMode');
-
+ this._X11.CreateMethod('XSync');
this._X11.CreateMethod('XBlackPixel');
this._X11.CreateMethod('XWhitePixel');
}
diff --git a/agents/modules_meshcore_min/monitor-info.min.js b/agents/modules_meshcore_min/monitor-info.min.js
index 2a6fe3ee..09261cea 100644
--- a/agents/modules_meshcore_min/monitor-info.min.js
+++ b/agents/modules_meshcore_min/monitor-info.min.js
@@ -1 +1 @@
-var promise=require("promise");var PPosition=4;var PSize=8;var _NET_WM_STATE_REMOVE=0;var _NET_WM_STATE_ADD=1;var _NET_WM_STATE_TOGGLE=2;var SubstructureRedirectMask=(1<<20);var SubstructureNotifyMask=(1<<19);function getLibInfo(f){if(process.platform!="linux"){throw ("Only supported on linux")}var a=require("child_process").execFile("/bin/sh",["sh"]);a.stdout.str="";a.stdout.on("data",function(i){this.str+=i.toString()});a.stdin.write("ldconfig -p | grep '"+f+".so.'\nexit\n");a.waitExit();var l=[];var g=a.stdout.str.split("\n");for(var b in g){if(g[b]){var c=g[b].split("=>");var k=c[1].trim();var e=c[0].trim().split(" ");var d=e[0];var h=e[1].substring(1,e[1].length-1).split(",");if(d.startsWith(f+".so.")){l.push({lib:d,path:k,info:h})}}}return(l)}function monitorinfo(){this._ObjectID="monitor-info";this._gm=require("_GenericMarshal");if(process.platform=="win32"){this._user32=this._gm.CreateNativeProxy("user32.dll");this._user32.CreateMethod("EnumDisplayMonitors");this._kernel32=this._gm.CreateNativeProxy("kernel32.dll");this._kernel32.CreateMethod("GetLastError");this.getInfo=function h(){var e=this;return(new promise(function(v,u){this._monitorinfo={resolver:v,rejector:u,self:e,callback:e._gm.GetGenericGlobalCallback(4)};this._monitorinfo.callback.info=this._monitorinfo;this._monitorinfo.dwData=e._gm.ObjectToPtr(this._monitorinfo);this._monitorinfo.callback.results=[];this._monitorinfo.callback.on("GlobalCallback",function i(x,w,y,A){if(this.ObjectToPtr_Verify(this.info,A)){var z=y.Deref(0,16).toBuffer();this.results.push({left:z.readInt32LE(0),top:z.readInt32LE(4),right:z.readInt32LE(8),bottom:z.readInt32LE(12)});var y=this.info.self._gm.CreateInteger();y.Val=1;return(y)}});if(e._user32.EnumDisplayMonitors(0,0,this._monitorinfo.callback,this._monitorinfo.dwData).Val==0){u("LastError="+e._kernel32.GetLastError().Val);return}else{v(this._monitorinfo.callback.results)}}))}}else{if(process.platform=="linux"){var a=false;try{if(require("user-sessions").isRoot()){a=true}}catch(b){}if(a){var r=getLibInfo("libX11");var t=getLibInfo("libXtst");var s=getLibInfo("libXext");var n;for(n in r){try{this._gm.CreateNativeProxy(r[n].path);Object.defineProperty(this,"Location_X11LIB",{value:r[n].path});break}catch(c){}}for(n in t){try{this._gm.CreateNativeProxy(t[n].path);Object.defineProperty(this,"Location_X11TST",{value:t[n].path});break}catch(c){}}for(n in s){try{this._gm.CreateNativeProxy(s[n].path);Object.defineProperty(this,"Location_X11EXT",{value:s[n].path});break}catch(c){}}}else{var g=require("fs");var d=g.readdirSync("/usr/lib");var f;for(var l in d){try{if(d[l].split("libX11.so.").length>1&&d[l].split(".").length==3){Object.defineProperty(this,"Location_X11LIB",{value:"/usr/lib/"+d[l]})}if(d[l].split("libXtst.so.").length>1&&d[l].split(".").length==3){Object.defineProperty(this,"Location_X11TST",{value:"/usr/lib/"+d[l]})}if(d[l].split("libXext.so.").length>1&&d[l].split(".").length==3){Object.defineProperty(this,"Location_X11EXT",{value:"/usr/lib/"+d[l]})}if(d[l].split("-linux-").length>1){f=g.readdirSync("/usr/lib/"+d[l]);for(j in f){if(f[j].split("libX11.so.").length>1&&f[j].split(".").length==3){Object.defineProperty(this,"Location_X11LIB",{value:"/usr/lib/"+d[l]+"/"+f[j]})}if(f[j].split("libXtst.so.").length>1&&f[j].split(".").length==3){Object.defineProperty(this,"Location_X11TST",{value:"/usr/lib/"+d[l]+"/"+f[j]})}if(f[j].split("libXext.so.").length>1&&f[j].split(".").length==3){Object.defineProperty(this,"Location_X11EXT",{value:"/usr/lib/"+d[l]+"/"+f[j]})}}}}catch(c){}}}Object.defineProperty(this,"kvm_x11_support",{value:(this.Location_X11LIB&&this.Location_X11TST&&this.Location_X11EXT)?true:false});if(this.Location_X11LIB){this._X11=this._gm.CreateNativeProxy(this.Location_X11LIB);this._X11.CreateMethod("XChangeProperty");this._X11.CreateMethod("XCloseDisplay");this._X11.CreateMethod("XCreateGC");this._X11.CreateMethod("XCreateWindow");this._X11.CreateMethod("XCreateSimpleWindow");this._X11.CreateMethod("XDefaultColormap");this._X11.CreateMethod("XDefaultScreen");this._X11.CreateMethod("XDrawLine");this._X11.CreateMethod("XDisplayHeight");this._X11.CreateMethod("XDisplayWidth");this._X11.CreateMethod("XFetchName");this._X11.CreateMethod("XFlush");this._X11.CreateMethod("XFree");this._X11.CreateMethod("XCreateGC");this._X11.CreateMethod("XGetWindowProperty");this._X11.CreateMethod("XInternAtom");this._X11.CreateMethod("XMapWindow");this._X11.CreateMethod({method:"XNextEvent",threadDispatch:true});this._X11.CreateMethod("XOpenDisplay");this._X11.CreateMethod("XRootWindow");this._X11.CreateMethod("XScreenCount");this._X11.CreateMethod("XScreenOfDisplay");this._X11.CreateMethod("XSelectInput");this._X11.CreateMethod("XSendEvent");this._X11.CreateMethod("XSetForeground");this._X11.CreateMethod("XSetFunction");this._X11.CreateMethod("XSetLineAttributes");this._X11.CreateMethod("XSetNormalHints");this._X11.CreateMethod("XSetSubwindowMode");this._X11.CreateMethod("XBlackPixel");this._X11.CreateMethod("XWhitePixel")}this.isUnity=function m(){return(process.env.XDG_CURRENT_DESKTOP=="Unity")};this.unDecorateWindow=function q(e,v){var i=this._gm.CreateVariable(40);var u=this._X11.XInternAtom(e,this._gm.CreateVariable("_MOTIF_WM_HINTS"),0);i.Deref(0,4).toBuffer().writeUInt32LE(1<<1);this._X11.XChangeProperty(e,v,u,u,32,0,i,5)};this.setWindowSizeHints=function p(e,w,z,A,v,i){var u=this._gm.CreateVariable(80);u.Deref(0,4).toBuffer().writeUInt32LE(PPosition|PSize);u.Deref(8,4).toBuffer().writeUInt32LE(z);u.Deref(12,4).toBuffer().writeUInt32LE(A);u.Deref(16,4).toBuffer().writeUInt32LE(v);u.Deref(20,4).toBuffer().writeUInt32LE(i);this._X11.XSetNormalHints(e,w,u)};this.setAlwaysOnTop=function o(e,i,u){var v=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE"),1);var w=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE_ABOVE"),1);var x=this._gm.CreateVariable(96);x.Deref(0,4).toBuffer().writeUInt32LE(33);x.Deref(48,4).toBuffer().writeUInt32LE(32);v.pointerBuffer().copy(x.Deref(40,8).toBuffer());x.Deref(56,8).toBuffer().writeUInt32LE(_NET_WM_STATE_ADD);w.pointerBuffer().copy(x.Deref(64,8).toBuffer());u.pointerBuffer().copy(x.Deref(32,8).toBuffer());this._X11.XSendEvent(e,i,0,SubstructureRedirectMask|SubstructureNotifyMask,x)};this.hideWindowIcon=function k(e,i,u){var v=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE"),1);var w=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE_SKIP_TASKBAR"),1);var x=this._gm.CreateVariable(96);x.Deref(0,4).toBuffer().writeUInt32LE(33);x.Deref(48,4).toBuffer().writeUInt32LE(32);v.pointerBuffer().copy(x.Deref(40,8).toBuffer());x.Deref(56,8).toBuffer().writeUInt32LE(_NET_WM_STATE_ADD);w.pointerBuffer().copy(x.Deref(64,8).toBuffer());u.pointerBuffer().copy(x.Deref(32,8).toBuffer());this._X11.XSendEvent(e,i,0,SubstructureRedirectMask|SubstructureNotifyMask,x)};this.getInfo=function h(){var e=this;return(new promise(function(x,w){var u=e._X11.XOpenDisplay(e._gm.CreateVariable(":0"));var A=e._X11.XScreenCount(u).Val;var y=[];for(var v=0;v");var k=c[1].trim();var e=c[0].trim().split(" ");var d=e[0];var h=e[1].substring(1,e[1].length-1).split(",");if(d.startsWith(f+".so.")){l.push({lib:d,path:k,info:h})}}}return(l)}function monitorinfo(){this._ObjectID="monitor-info";this._gm=require("_GenericMarshal");if(process.platform=="win32"){this._user32=this._gm.CreateNativeProxy("user32.dll");this._user32.CreateMethod("EnumDisplayMonitors");this._kernel32=this._gm.CreateNativeProxy("kernel32.dll");this._kernel32.CreateMethod("GetLastError");this.getInfo=function h(){var e=this;return(new promise(function(v,u){this._monitorinfo={resolver:v,rejector:u,self:e,callback:e._gm.GetGenericGlobalCallback(4)};this._monitorinfo.callback.info=this._monitorinfo;this._monitorinfo.dwData=e._gm.ObjectToPtr(this._monitorinfo);this._monitorinfo.callback.results=[];this._monitorinfo.callback.on("GlobalCallback",function i(x,w,y,A){if(this.ObjectToPtr_Verify(this.info,A)){var z=y.Deref(0,16).toBuffer();this.results.push({left:z.readInt32LE(0),top:z.readInt32LE(4),right:z.readInt32LE(8),bottom:z.readInt32LE(12)});var y=this.info.self._gm.CreateInteger();y.Val=1;return(y)}});if(e._user32.EnumDisplayMonitors(0,0,this._monitorinfo.callback,this._monitorinfo.dwData).Val==0){u("LastError="+e._kernel32.GetLastError().Val);return}else{v(this._monitorinfo.callback.results)}}))}}else{if(process.platform=="linux"){var a=false;try{if(require("user-sessions").isRoot()){a=true}}catch(b){}if(a){var r=getLibInfo("libX11");var t=getLibInfo("libXtst");var s=getLibInfo("libXext");var n;for(n in r){try{this._gm.CreateNativeProxy(r[n].path);Object.defineProperty(this,"Location_X11LIB",{value:r[n].path});break}catch(c){}}for(n in t){try{this._gm.CreateNativeProxy(t[n].path);Object.defineProperty(this,"Location_X11TST",{value:t[n].path});break}catch(c){}}for(n in s){try{this._gm.CreateNativeProxy(s[n].path);Object.defineProperty(this,"Location_X11EXT",{value:s[n].path});break}catch(c){}}}else{var g=require("fs");var d=g.readdirSync("/usr/lib");var f;for(var l in d){try{if(d[l].split("libX11.so.").length>1&&d[l].split(".").length==3){Object.defineProperty(this,"Location_X11LIB",{value:"/usr/lib/"+d[l]})}if(d[l].split("libXtst.so.").length>1&&d[l].split(".").length==3){Object.defineProperty(this,"Location_X11TST",{value:"/usr/lib/"+d[l]})}if(d[l].split("libXext.so.").length>1&&d[l].split(".").length==3){Object.defineProperty(this,"Location_X11EXT",{value:"/usr/lib/"+d[l]})}if(d[l].split("-linux-").length>1){f=g.readdirSync("/usr/lib/"+d[l]);for(j in f){if(f[j].split("libX11.so.").length>1&&f[j].split(".").length==3){Object.defineProperty(this,"Location_X11LIB",{value:"/usr/lib/"+d[l]+"/"+f[j]})}if(f[j].split("libXtst.so.").length>1&&f[j].split(".").length==3){Object.defineProperty(this,"Location_X11TST",{value:"/usr/lib/"+d[l]+"/"+f[j]})}if(f[j].split("libXext.so.").length>1&&f[j].split(".").length==3){Object.defineProperty(this,"Location_X11EXT",{value:"/usr/lib/"+d[l]+"/"+f[j]})}}}}catch(c){}}}Object.defineProperty(this,"kvm_x11_support",{value:(this.Location_X11LIB&&this.Location_X11TST&&this.Location_X11EXT)?true:false});if(this.Location_X11LIB){this._X11=this._gm.CreateNativeProxy(this.Location_X11LIB);this._X11.CreateMethod("XChangeProperty");this._X11.CreateMethod("XCloseDisplay");this._X11.CreateMethod("XConnectionNumber");this._X11.CreateMethod("XConvertSelection");this._X11.CreateMethod("XCreateGC");this._X11.CreateMethod("XCreateWindow");this._X11.CreateMethod("XCreateSimpleWindow");this._X11.CreateMethod("XDefaultColormap");this._X11.CreateMethod("XDefaultScreen");this._X11.CreateMethod("XDestroyWindow");this._X11.CreateMethod("XDrawLine");this._X11.CreateMethod("XDisplayHeight");this._X11.CreateMethod("XDisplayWidth");this._X11.CreateMethod("XFetchName");this._X11.CreateMethod("XFlush");this._X11.CreateMethod("XFree");this._X11.CreateMethod("XCreateGC");this._X11.CreateMethod("XGetWindowProperty");this._X11.CreateMethod("XInternAtom");this._X11.CreateMethod("XMapWindow");this._X11.CreateMethod({method:"XNextEvent",threadDispatch:true});this._X11.CreateMethod({method:"XNextEvent",newName:"XNextEventSync"});this._X11.CreateMethod("XOpenDisplay");this._X11.CreateMethod("XPending");this._X11.CreateMethod("XRootWindow");this._X11.CreateMethod("XSelectInput");this._X11.CreateMethod("XScreenCount");this._X11.CreateMethod("XScreenOfDisplay");this._X11.CreateMethod("XSelectInput");this._X11.CreateMethod("XSendEvent");this._X11.CreateMethod("XSetForeground");this._X11.CreateMethod("XSetFunction");this._X11.CreateMethod("XSetLineAttributes");this._X11.CreateMethod("XSetNormalHints");this._X11.CreateMethod("XSetSubwindowMode");this._X11.CreateMethod("XSync");this._X11.CreateMethod("XBlackPixel");this._X11.CreateMethod("XWhitePixel")}this.isUnity=function m(){return(process.env.XDG_CURRENT_DESKTOP=="Unity")};this.unDecorateWindow=function q(e,v){var i=this._gm.CreateVariable(40);var u=this._X11.XInternAtom(e,this._gm.CreateVariable("_MOTIF_WM_HINTS"),0);i.Deref(0,4).toBuffer().writeUInt32LE(1<<1);this._X11.XChangeProperty(e,v,u,u,32,0,i,5)};this.setWindowSizeHints=function p(e,w,z,A,v,i){var u=this._gm.CreateVariable(80);u.Deref(0,4).toBuffer().writeUInt32LE(PPosition|PSize);u.Deref(8,4).toBuffer().writeUInt32LE(z);u.Deref(12,4).toBuffer().writeUInt32LE(A);u.Deref(16,4).toBuffer().writeUInt32LE(v);u.Deref(20,4).toBuffer().writeUInt32LE(i);this._X11.XSetNormalHints(e,w,u)};this.setAlwaysOnTop=function o(e,i,u){var v=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE"),1);var w=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE_ABOVE"),1);var x=this._gm.CreateVariable(96);x.Deref(0,4).toBuffer().writeUInt32LE(33);x.Deref(48,4).toBuffer().writeUInt32LE(32);v.pointerBuffer().copy(x.Deref(40,8).toBuffer());x.Deref(56,8).toBuffer().writeUInt32LE(_NET_WM_STATE_ADD);w.pointerBuffer().copy(x.Deref(64,8).toBuffer());u.pointerBuffer().copy(x.Deref(32,8).toBuffer());this._X11.XSendEvent(e,i,0,SubstructureRedirectMask|SubstructureNotifyMask,x)};this.hideWindowIcon=function k(e,i,u){var v=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE"),1);var w=this._X11.XInternAtom(e,this._gm.CreateVariable("_NET_WM_STATE_SKIP_TASKBAR"),1);var x=this._gm.CreateVariable(96);x.Deref(0,4).toBuffer().writeUInt32LE(33);x.Deref(48,4).toBuffer().writeUInt32LE(32);v.pointerBuffer().copy(x.Deref(40,8).toBuffer());x.Deref(56,8).toBuffer().writeUInt32LE(_NET_WM_STATE_ADD);w.pointerBuffer().copy(x.Deref(64,8).toBuffer());u.pointerBuffer().copy(x.Deref(32,8).toBuffer());this._X11.XSendEvent(e,i,0,SubstructureRedirectMask|SubstructureNotifyMask,x)};this.getInfo=function h(){var e=this;return(new promise(function(x,w){var u=e._X11.XOpenDisplay(e._gm.CreateVariable(":0"));var A=e._X11.XScreenCount(u).Val;var y=[];for(var v=0;v = domain.limits.maxusersessions) {
+ if (((typeof domain.limits.maxusersessions == 'number') && (domainUserSessionCount >= domain.limits.maxusersessions)) || ((typeof domain.limits.maxsingleusersessions == 'number') && (selfUserSessionCount >= domain.limits.maxsingleusersessions))) {
ws.send(JSON.stringify({ action: 'stopped', msg: 'Session count exceed' }));
try { obj.ws.close(); } catch (e) { }
return;
@@ -708,6 +708,45 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
break;
}
+ case 'changepassword':
+ {
+ // Change our own password
+ if (obj.common.validateString(command.oldpass, 1, 256) == false) break;
+ if (obj.common.validateString(command.newpass, 1, 256) == false) break;
+ if (obj.common.validateString(command.hint, 0, 256) == false) break;
+ if (obj.common.checkPasswordRequirements(command.newpass, domain.passwordrequirements) == false) break; // Password does not meet requirements
+
+ // Start by checking the old password
+ obj.parent.checkUserPassword(domain, user, command.oldpass, function (result) {
+ if (result == true) {
+ // Update the password
+ require('./pass').hash(command.newpass, function (err, salt, hash) {
+ if (err) {
+ // Send user notification of error
+ displayNotificationMessage('Error, password not changed.');
+ } else {
+ // Change the password
+ var hint = command.hint;
+ if (hint.length > 250) hint = hint.substring(0, 250);
+ user.salt = salt;
+ user.hash = hash;
+ user.passhint = req.body.apasswordhint;
+ user.passchange = Math.floor(Date.now() / 1000);
+ delete user.passtype;
+ obj.db.SetUser(user);
+ obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
+
+ // Send user notification of password change
+ displayNotificationMessage('Password changed.');
+ }
+ });
+ } else {
+ // Send user notification of error
+ displayNotificationMessage('Current password not correct.');
+ }
+ });
+ break;
+ }
case 'changeuserpass':
{
// Change a user's password
@@ -718,22 +757,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (typeof command.removeMultiFactor != 'boolean') break;
if (obj.common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) break; // Password does not meet requirements
- var chguserid = 'user/' + domain.id + '/' + command.user.toLowerCase(), chguser = obj.parent.users[chguserid];
- if (chguser && chguser.salt) {
+ var chguser = obj.parent.users['user/' + domain.id + '/' + command.user.toLowerCase()];
+ if (chguser) {
// Compute the password hash & save it
- require('./pass').hash(command.pass, chguser.salt, function (err, hash) {
+ require('./pass').hash(command.pass, function (err, salt, hash) {
if (!err) {
var annonceChange = false;
+ chguser.salt = salt;
chguser.hash = hash;
chguser.passhint = command.hint;
+ chguser.passchange = Math.floor(Date.now() / 1000);
+ delete chguser.passtype; // Remove the password type if one was present.
if (command.removeMultiFactor == true) {
if (chguser.otpsecret) { delete chguser.otpsecret; annonceChange = true; }
if (chguser.otphkeys) { delete chguser.otphkeys; annonceChange = true; }
if (chguser.otpkeys) { delete chguser.otpkeys; annonceChange = true; }
}
obj.db.SetUser(chguser);
-
if (annonceChange == true) { obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Removed 2nd factor auth.', domain: domain.id }); }
+ } else {
+ // Report that the password change failed
+ // TODO
}
});
}
@@ -1775,6 +1819,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
}
+ // Display a notification message for this session only.
+ function displayNotificationMessage(msg, tag) { ws.send(JSON.stringify({ "action": "msg", "type": "notify", "value": msg, "userid": user._id, "username": user.name, "tag": tag })); }
+
// Read the folder and all sub-folders and serialize that into json.
function readFilesRec(path) {
var r = {}, dir = obj.fs.readdirSync(path);
diff --git a/package.json b/package.json
index 19903921..2a881693 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "meshcentral",
- "version": "0.2.8-b",
+ "version": "0.2.8-c",
"keywords": [
"Remote Management",
"Intel AMT",
diff --git a/views/default-min.handlebars b/views/default-min.handlebars
index 041ab905..ad81ff85 100644
--- a/views/default-min.handlebars
+++ b/views/default-min.handlebars
@@ -1 +1 @@
- MeshCentral
↔
My Devices My Account My Events My Files My Users My Server
General Desktop Terminal Files Events Intel® AMT Console
Server disconnected , click to reconnect .
My Account Device Groups (
New )
My Files These files are shared publicly, click "link" to get public url.
✓
✗
My Server Server actions
Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Show
Last 60 Last 120 Last 250 Last 500 Last 1000
General -
Events -
Show
Last 60 Last 120 Last 250 Last 500 Last 1000
Local file upload Server file selection File Selection
Agent Remote Desktop 100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Scaling
Fast Medium Slow Very slow Frame rate
Intel® AMT Hardware KVM RLE8, Fastest RLE16, Recommended RAW8, Slow RAW16, Very Slow Image Encoding
\ No newline at end of file
+ MeshCentral
↔
My Devices My Account My Events My Files My Users My Server
General Desktop Terminal Files Events Intel® AMT Console
Server disconnected , click to reconnect .
My Account Device Groups (
New )
My Files These files are shared publicly, click "link" to get public url.
✓
✗
My Server Server actions
Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Show
Last 60 Last 120 Last 250 Last 500 Last 1000
General -
Events -
Show
Last 60 Last 120 Last 250 Last 500 Last 1000
Local file upload Server file selection File Selection
Agent Remote Desktop 100% 87.5% 75% 62.5% 50% 37.5% 25% 12.5% Scaling
Fast Medium Slow Very slow Frame rate
Intel® AMT Hardware KVM RLE8, Fastest RLE16, Recommended RAW8, Slow RAW16, Very Slow Image Encoding
\ No newline at end of file
diff --git a/views/default.handlebars b/views/default.handlebars
index cb284857..70b95108 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -5479,20 +5479,31 @@
function account_showChangePassword() {
if (xxdialogMode) return;
- var x = "Change your account password by entering the new password twice in the boxes below. ";
- x += "';
- setDialogMode(2, "Change Password", 0, null, x);
- Q('apassword1').focus();
+ var x = "Change your account password by entering the old password and new password twice in the boxes below. Password hint can be used but is not recommanded. ";
+ //x += "';
+ setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x);
+ Q('apassword0').focus();
account_validateNewPassword();
}
+ function account_showChangePasswordEx() {
+ if (Q('apassword1').value == Q('apassword2').value) {
+ meshserver.send({ action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value, hint: Q('apasswordhint').value });
+ }
+ }
+
function account_createMesh() {
if (xxdialogMode) return;
@@ -5522,7 +5533,7 @@
}
function account_validateNewPassword() {
- var r = '', ok = (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value);
+ var r = '', ok = (Q('apassword0').value.length > 0) && (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value) && (Q('apassword0').value != Q('apassword1').value) && (Q('apasswordhint').value != Q('apassword1').value);
if (Q('apassword1').value != '') {
if (passRequirements == null || passRequirements == '') {
// No password requirements, display password strength
@@ -5535,7 +5546,8 @@
}
}
QH('dxPassWarn', r);
- QE('account_dlgOkButton', ok);
+ //QE('account_dlgOkButton', ok);
+ QE('idx_dlgOkButton', ok);
}
// Return a password strength score
@@ -6344,6 +6356,7 @@
x += addHtmlValue('Email', ' ');
x += addHtmlValue('Password', ' ');
x += addHtmlValue('Password', ' ');
+ if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += 'Requirements: ' + r.join(', ') + '.
'; }
setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
showCreateNewAccountDialogValidate();
Q('p4name').focus();
@@ -6351,7 +6364,9 @@
function showCreateNewAccountDialogValidate(x) {
if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
- QE('idx_dlgOkButton', (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements));
+ var ok = (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements);
+ if (ok && passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } }
+ QE('idx_dlgOkButton', ok);
}
function showCreateNewAccountDialogEx() {
@@ -6440,8 +6455,8 @@
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
// Server permissions
- var msg = '';
- if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { msg += "Locked account, "; }
+ var msg = '', premsg = '';
+ if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = ' '; msg += 'Locked account, '; }
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "No server rights"; } else if (user.siteadmin == 8) { msg += "Access to server files"; } else if (user.siteadmin == 0xFFFFFFFF) { msg += "Full administrator"; } else { msg += "Partial rights"; }
// Show user attributes
@@ -6449,7 +6464,7 @@
var email = user.email?EscapeHtml(user.email):'Not set ', everify = '';
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'🗸 ':'🗴 '); }
x += addDeviceAttribute('Email', everify + " " + email + ' ');
- x += addDeviceAttribute('Server Rights', "" + msg + " ");
+ x += addDeviceAttribute('Server Rights', premsg + "" + msg + " ");
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
@@ -6460,7 +6475,7 @@
if (user.otpsecret > 0) { factors.push('Authentication App'); }
if (user.otphkeys > 0) { factors.push('Security Key'); }
if (user.otpkeys > 0) { factors.push('Backup Codes'); }
- x += addDeviceAttribute('Security', factors.join(', '));
+ x += addDeviceAttribute('Security', ' ' + factors.join(', '));
}
x += ' ';
@@ -6534,7 +6549,8 @@
x += addHtmlValue('Password', ' ');
x += addHtmlValue('Password', ' ');
x += addHtmlValue('Password hint', ' ');
- if (multiFactor == 1) { x += ' Remove all 2nd factor authentication.'; }
+ if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '
Requirements: ' + r.join(', ') + '.
'; }
+ if (multiFactor == 1) { x += ' Remove all 2nd factor authentication.
'; }
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
showCreateNewAccountDialogValidate(1);
Q('p4pass1').focus();
diff --git a/views/login-min.handlebars b/views/login-min.handlebars
index 658e8d88..d2d79696 100644
--- a/views/login-min.handlebars
+++ b/views/login-min.handlebars
@@ -1 +1 @@
- MeshCentral - Login
Welcome Connect to your home or office devices from anywhere in the world using MeshCentral , the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
\ No newline at end of file
+ MeshCentral - Login
Welcome Connect to your home or office devices from anywhere in the world using MeshCentral , the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
\ No newline at end of file
diff --git a/views/login.handlebars b/views/login.handlebars
index c7ea1213..e9aaec48 100644
--- a/views/login.handlebars
+++ b/views/login.handlebars
@@ -424,7 +424,7 @@
ok = false;
QS('nuPass1').color = '#7b241c';
QS('nuPass2').color = '#7b241c';
- QH('passWarning', 'Password Policy '); // TODO: Display problem hint
+ QH('passWarning', 'Password Policy '); // This is also a link to the password policy
} else {
QH('passWarning', '');
}
@@ -442,6 +442,18 @@
QE('createButton', ok);
}
+ function showPasswordPolicy() {
+ var policy = '
';
+ if (passRequirements.min) { policy += 'Minimum length: ' + passRequirements.min + ' '; }
+ if (passRequirements.max) { policy += 'Maximum length: ' + passRequirements.max + ' '; }
+ if (passRequirements.upper) { policy += 'Upper case: ' + passRequirements.upper + ' '; }
+ if (passRequirements.lower) { policy += 'Lower case: ' + passRequirements.lower + ' '; }
+ if (passRequirements.numeric) { policy += 'Numeric: ' + passRequirements.numeric + ' '; }
+ if (passRequirements.nonalpha) { policy += 'Non-alphanumeric: ' + passRequirements.nonalpha + ' '; }
+ policy += '
';
+ messagebox("Password Policy", policy);
+ }
+
function validateReset(e) {
setDialogMode(0);
var x = validateEmail(Q('remail').value);
diff --git a/webserver.js b/webserver.js
index de136f4b..2794b761 100644
--- a/webserver.js
+++ b/webserver.js
@@ -421,12 +421,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (checkUserOneTimePasswordRequired(domain, user)) {
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
if (result == false) {
+ var randomWaitTime = 0;
+
// 2-step auth is required, but the token is not present or not valid.
- if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '
Invalid token, try again. '; }
- req.session.loginmode = '4';
- req.session.tokenusername = xusername;
- req.session.tokenpassword = xpassword;
- res.redirect(domain.url);
+ if ((req.body.token != null) || (req.body.hwtoken != null)) {
+ randomWaitTime = 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095); // This is a fail, wait a random time. 2 to 6 seconds.
+ req.session.error = '
Invalid token, try again. ';
+ }
+
+ // Wait and redirect the user
+ setTimeout(function () {
+ req.session.loginmode = '4';
+ req.session.tokenusername = xusername;
+ req.session.tokenpassword = xpassword;
+ res.redirect(domain.url);
+ }, randomWaitTime);
} else {
// Login succesful
completeLoginRequest(req, res, domain, user, userid);
@@ -438,15 +447,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Login succesful
completeLoginRequest(req, res, domain, user, userid);
} else {
- //console.log('passhint', passhint);
- delete req.session.loginmode;
- if (err == 'locked') { req.session.error = '
Account locked. '; } else { req.session.error = '
Login failed, check username and password. '; }
- if ((passhint != null) && (passhint.length > 0)) {
- req.session.passhint = passhint;
- } else {
- delete req.session.passhint;
- }
- res.redirect(domain.url);
+ // Login failed, wait a random delay
+ setTimeout(function () {
+ // If the account is locked, display that.
+ if (err == 'locked') { req.session.error = '
Account locked. '; } else { req.session.error = '
Login failed, check username and password. '; }
+
+ // Clean up login mode and display password hint if present.
+ delete req.session.loginmode;
+ if ((passhint != null) && (passhint.length > 0)) {
+ req.session.passhint = passhint;
+ } else {
+ delete req.session.passhint;
+ }
+ res.redirect(domain.url);
+ }, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds.
}
});
}
@@ -547,6 +561,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (err) throw err;
user.salt = salt;
user.hash = hash;
+ delete user.passtype;
obj.db.SetUser(user);
// Send the verification email
@@ -688,6 +703,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
userinfo = obj.users[user._id];
userinfo.salt = salt;
userinfo.hash = hash;
+ delete userinfo.passtype;
userinfo.passchange = Math.floor(Date.now() / 1000);
userinfo.passhint = null;
//delete userinfo.otpsecret; // Currently a email password reset will turn off 2-step login.
@@ -758,28 +774,63 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
});
}
+ // Check a user's password
+ obj.checkUserPassword = function(domain, user, password, func) {
+ // Check the old password
+ if (user.passtype != null) {
+ // IIS default clear or weak password hashing (SHA-1)
+ require('./pass').iishash(user.passtype, password, user.salt, function (err, hash) {
+ if (err) return func(false);
+ if (hash == user.hash) {
+ if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { return func(false); } // Account is locked
+ return func(true); // Allow password change
+ }
+ func(false);
+ });
+ } else {
+ // Default strong password hashing (pbkdf2 SHA384)
+ require('./pass').hash(password, user.salt, function (err, hash) {
+ if (err) return func(false);
+ if (hash == user.hash) {
+ if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { return func(false); } // Account is locked
+ return func(true); // Allow password change
+ }
+ func(false);
+ });
+ }
+ }
+
// Handle password changes
function handlePasswordChangeRequest(req, res) {
const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) 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; }
+ if (!req.session || !req.session.userid || !req.body.apassword0 || !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;
- var hint = req.body.apasswordhint;
- if (hint.length > 250) hint = hint.substring(0, 250);
- var user = obj.users[req.session.userid];
- user.salt = salt;
- user.hash = hash;
- user.passchange = Math.floor(Date.now() / 1000);
- user.passhint = req.body.apasswordhint;
- obj.db.SetUser(user);
- req.session.viewmode = 2;
- res.redirect(domain.url);
- obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
+ // Get the current user
+ var user = obj.users[req.session.userid];
+ if (!user) { res.redirect(domain.url); return; }
+
+ // Check old password
+ obj.checkUserPassword(domain, user, req.body.apassword0, function (result) {
+ if (result == true) {
+ // Update the password
+ require('./pass').hash(req.body.apassword1, function (err, salt, hash) {
+ if (err) throw err;
+ var hint = req.body.apasswordhint;
+ if (hint.length > 250) hint = hint.substring(0, 250);
+ user.salt = salt;
+ user.hash = hash;
+ user.passhint = req.body.apasswordhint;
+ user.passchange = Math.floor(Date.now() / 1000);
+ delete user.passtype;
+ obj.db.SetUser(user);
+ req.session.viewmode = 2;
+ res.redirect(domain.url);
+ obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
+ });
+ }
});
}