Allows MeshCommander to relay thru MeshCentral.

Ylian Saint-Hilaire committed Oct 13, 2018 at 16:04 UTC 564646fcdf91b5aafde8fbfab8de0b05c9085346
3 files changed +56 -60
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.2.2-d",
3 + "version": "0.2.2-e",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
+2 -2
@@ -161,7 +161,7 @@
161 <td id=devListToolbar class=style14>
162 &nbsp;&nbsp;<input type="button" id="SelectAllButton" onclick="selectallButtonFunction();" value="Select All" />&nbsp;
163 <input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />&nbsp;
164 - <input id=SearchInput type=text style=width:120px placeholder=Search onchange=onSearchInputChanged() onkeyup=onSearchInputChanged() autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />&nbsp;
164 + <input id=SearchInput type=text style=width:120px placeholder=Filter onchange=onSearchInputChanged() onkeyup=onSearchInputChanged() autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />&nbsp;
165 <input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span>
166 </td>
167 <td id=kvmListToolbar class=style14 style=height:100%>
@@ -281,7 +281,7 @@
281 <td class=style14>
282 &nbsp;
283 <input type=button onclick=showCreateNewAccountDialog() value="New Account..." />&nbsp;
284 - <input id=UserSearchInput type=text style=width:120px placeholder=Search onchange=onUserSearchInputChanged() onkeyup=onUserSearchInputChanged() autocomplete=off onfocus=onUserSearchFocus(1) onblur=onUserSearchFocus(0) />&nbsp;
284 + <input id=UserSearchInput type=text style=width:120px placeholder=Filter onchange=onUserSearchInputChanged() onkeyup=onUserSearchInputChanged() autocomplete=off onfocus=onUserSearchFocus(1) onblur=onUserSearchFocus(0) />&nbsp;
285 </td>
286 <td class=h2></td>
287 </tr>
webserver.js
+53 -57
@@ -1081,8 +1081,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1081 if (!user) { console.log('ERR: Not a user'); return; }
1082 Debug(1, 'Websocket relay connected from ' + user.name + ' for ' + req.query.host + '.');
1083
1084 - ws.pause(); // Hold this socket until we are ready.
1085 - ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
1084 + ws.pause(); // Hold this socket until we are ready.
1085 + try { ws._socket.setKeepAlive(true, 240000); } catch (ex) { } // Set TCP keep alive
1086
1087 // Fetch information about the target
1088 obj.db.Get(req.query.host, function (err, docs) {
@@ -1150,7 +1150,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1150 if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } }
1151 };
1152
1153 - // Handke CIRA tunnel state change
1153 + // Handle CIRA tunnel state change
1154 chnl.onStateChange = function (ciraconn, state) {
1155 Debug(2, 'Relay TLS CIRA state change', state);
1156 if (state == 0) { try { ws.close(); } catch (e) { } }
@@ -1169,6 +1169,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1169 try {
1170 data = data.toString('binary');
1171 if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
1172 + //ws.send(Buffer.from(data, 'binary'));
1173 ws.send(data);
1174 } catch (e) { }
1175 });
@@ -1209,7 +1210,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1210 ws.forwardclient.onData = function (ciraconn, data) {
1211 Debug(4, 'Relay CIRA data', data.length);
1212 if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
1212 - if (data.length > 0) { try { ws.send(data); } catch (e) { } } // TODO: Add TLS support
1213 + if (data.length > 0) { try { ws.send(new Buffer(data, 'binary')); } catch (e) { } } // TODO: Add TLS support
1214 };
1215
1216 ws.forwardclient.onSendOk = function (ciraconn) {
@@ -1801,8 +1802,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1802 obj.app.post(url + 'resetaccount', handleResetAccountRequest);
1803 obj.app.get(url + 'checkmail', handleCheckMailRequest);
1804 obj.app.post(url + 'amtevents.ashx', obj.handleAmtEventRequest);
1804 - obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
1805 - obj.app.ws(url + 'webrelay.ashx', handleRelayWebSocket);
1805 obj.app.get(url + 'meshagents', obj.handleMeshAgentRequest);
1806 obj.app.get(url + 'meshosxagent', obj.handleMeshOsxAgentRequest);
1807 obj.app.get(url + 'meshsettings', obj.handleMeshSettingsRequest);
@@ -1812,57 +1811,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1811 obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
1812 obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
1813 obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } });
1815 -
1816 - // User login
1817 - obj.app.ws(url + 'control.ashx', function (ws, req) {
1818 - try {
1819 - var domain = checkUserIpAddress(ws, req);
1820 - if (domain != null) {
1821 - var loginok = false;
1822 - // Check if the user is logged in
1823 - if ((!req.session) || (!req.session.userid) || (req.session.domainid != domain.id)) {
1824 - // If a default user is active, setup the session here.
1825 - if (obj.args.user && obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) {
1826 - if (req.session && req.session.loginmode) { delete req.session.loginmode; }
1827 - req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
1828 - req.session.domainid = domain.id;
1829 - req.session.currentNode = '';
1830 - obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); // Accept the connection
1831 - loginok = true;
1832 - } else {
1833 - // See the the user/pass is provided in URL arguments
1834 - if ((req.query.user != null) && (req.query.pass != null)) {
1835 - loginok = true;
1836 - obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
1837 - var loginok2 = false;
1838 - if (err == null) {
1839 - var user = obj.users[userid];
1840 - if (user) {
1841 - req.session.userid = userid;
1842 - req.session.domainid = domain.id;
1843 - req.session.currentNode = '';
1844 - obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); // Accept the connection
1845 - loginok2 = true;
1846 - }
1847 - }
1848 - if (loginok2 == false) {
1849 - // Close the websocket connection
1850 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { }
1851 - }
1852 - });
1853 - }
1854 - }
1855 - } else {
1856 - obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws, req, obj.args, domain); // Accept the connection
1857 - loginok = true;
1858 - }
1859 - if (loginok == false) {
1860 - // Close the websocket connection
1861 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { }
1862 - }
1863 - }
1864 - } catch (e) { console.log(e); }
1865 - });
1814 + obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); });
1815 + obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformSessionAuth(ws, req, handleRelayWebSocket); });
1816 + obj.app.ws(url + 'control.ashx', function (ws, req) { PerformSessionAuth(ws, req, function (ws1, req1, domain) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain); }); });
1817
1818 // Server picture
1819 obj.app.get(url + 'serverpic.ashx', function (req, res) {
@@ -1895,6 +1846,51 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1846 obj.app.use(url, obj.express.static(obj.path.join(__dirname, 'public')));
1847 }
1848
1849 + // Authenticates a session and forwards
1850 + function PerformSessionAuth(ws, req, func) {
1851 + try {
1852 + var domain = checkUserIpAddress(ws, req);
1853 + if (domain != null) {
1854 + var loginok = false;
1855 + // Check if the user is logged in
1856 + if ((!req.session) || (!req.session.userid) || (req.session.domainid != domain.id)) {
1857 + // If a default user is active, setup the session here.
1858 + if (obj.args.user && obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) {
1859 + if (req.session && req.session.loginmode) { delete req.session.loginmode; }
1860 + req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase();
1861 + req.session.domainid = domain.id;
1862 + func(ws, req, domain);
1863 + loginok = true;
1864 + } else {
1865 + // See the the user/pass is provided in URL arguments
1866 + if ((req.query.user != null) && (req.query.pass != null)) {
1867 + loginok = true;
1868 + obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
1869 + var loginok2 = false;
1870 + if (err == null) {
1871 + var user = obj.users[userid];
1872 + if (user) {
1873 + req.session.userid = userid;
1874 + req.session.domainid = domain.id;
1875 + func(ws, req, domain);
1876 + loginok2 = true;
1877 + }
1878 + }
1879 + // If not authenticated, close the websocket connection
1880 + if (loginok2 == false) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } }
1881 + });
1882 + }
1883 + }
1884 + } else {
1885 + func(ws, req, domain);
1886 + loginok = true;
1887 + }
1888 + // If not authenticated, close the websocket connection
1889 + if (loginok == false) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } }
1890 + }
1891 + } catch (e) { console.log(e); }
1892 + }
1893 +
1894 // Find a free port starting with the specified one and going up.
1895 function CheckListenPort(port, func) {
1896 var s = obj.net.createServer(function (socket) { });