diff --git a/agents/MeshCentralRouter.exe b/agents/MeshCentralRouter.exe index 56ce7aae..579538dd 100644 Binary files a/agents/MeshCentralRouter.exe and b/agents/MeshCentralRouter.exe differ diff --git a/certoperations.js b/certoperations.js index 425a4206..dce5cc7b 100644 --- a/certoperations.js +++ b/certoperations.js @@ -854,7 +854,7 @@ module.exports.CertificateOperations = function (parent) { obj.acceleratorPerformOperation = function (operation, data, tag, func) { if (acceleratorTotalCount <= 1) { // No accelerators available - program.processMessage({ action: operation, data: data, tag: tag, func: func }); + require(program).processMessage({ action: operation, data: data, tag: tag, func: func }); } else { var acc = obj.getAccelerator(); if (acc == null) { diff --git a/letsEncrypt.js b/letsEncrypt.js index d2eda15d..b01b559e 100644 --- a/letsEncrypt.js +++ b/letsEncrypt.js @@ -261,13 +261,28 @@ module.exports.CreateLetsEncrypt = function (parent) { // GreenLock v3 Manager module.exports.create = function (options) { + //console.log('xxx-create', options); var manager = { parent: globalLetsEncrypt }; manager.find = async function (options) { - //console.log('LE-FIND', options); + try { + // GreenLock sometimes has the bad behavior of adding a wildcard cert request, remove it here if needed. + if ((options.wildname != null) && (options.wildname != '')) { options.wildname = ''; } + if (options.altnames) { + var altnames2 = []; + for (var i in options.altnames) { if (options.altnames[i].indexOf('*') == -1) { altnames2.push(options.altnames[i]); } } + options.altnames = altnames2; + } + if (options.servernames) { + var servernames2 = []; + for (var i in options.servernames) { if (options.servernames[i].indexOf('*') == -1) { servernames2.push(options.servernames[i]); } } + options.servernames = servernames2; + } + } catch (ex) { console.log(ex); } return Promise.resolve([{ subject: options.servername, altnames: options.altnames }]); }; manager.set = function (options) { + //console.log('xxx-set', options); manager.parent.parent.debug('cert', "Certificate has been set: " + JSON.stringify(options)); if (manager.parent.parent.config.letsencrypt.production == manager.parent.runAsProduction) { manager.parent.performRestart = true; } else if ((manager.parent.parent.config.letsencrypt.production === true) && (manager.parent.runAsProduction === false)) { manager.parent.performMoveToProduction = true; } @@ -275,6 +290,7 @@ module.exports.create = function (options) { }; manager.remove = function (options) { + //console.log('xxx-remove', options); manager.parent.parent.debug('cert', "Certificate has been removed: " + JSON.stringify(options)); if (manager.parent.parent.config.letsencrypt.production == manager.parent.runAsProduction) { manager.parent.performRestart = true; } else if ((manager.parent.parent.config.letsencrypt.production === true) && (manager.parent.runAsProduction === false)) { manager.parent.performMoveToProduction = true; } @@ -283,6 +299,7 @@ module.exports.create = function (options) { // set the global config manager.defaults = async function (options) { + //console.log('xxx-defaults', options); var r; if (manager.parent.runAsProduction === true) { // Production diff --git a/meshcentral.js b/meshcentral.js index d2c72aef..a30756d9 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -1079,6 +1079,7 @@ function CreateMeshCentralServer(config, args) { var leok = true; if (typeof obj.config.letsencrypt.email != 'string') { leok = false; addServerWarning("Missing Let's Encrypt email address."); } else if (typeof obj.config.letsencrypt.names != 'string') { leok = false; addServerWarning("Invalid Let's Encrypt host names."); } + else if (obj.config.letsencrypt.names.indexOf('*') >= 0) { leok = false; addServerWarning("Invalid Let's Encrypt names, can't contain a *."); } else if (obj.config.letsencrypt.email.split('@').length != 2) { leok = false; addServerWarning("Invalid Let's Encrypt email address."); } else if (obj.config.letsencrypt.email.trim() !== obj.config.letsencrypt.email) { leok = false; addServerWarning("Invalid Let's Encrypt email address."); } else { diff --git a/package.json b/package.json index deab06d3..fe4e8505 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.4.9-o", + "version": "0.4.9-q", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/scripts/amt-0.2.0.js b/public/scripts/amt-0.2.0.js index 9fefec20..4cce0d0f 100644 --- a/public/scripts/amt-0.2.0.js +++ b/public/scripts/amt-0.2.0.js @@ -760,7 +760,7 @@ function AmtStackCreateService(wsmanStack) { // ###BEGIN###{Certificates} // Forge MD5 -function hex_md5(str) { return forge.md.md5.create().update(str).digest().toHex(); } +function hex_md5(str) { if (str == null) { str = ''; } return forge.md.md5.create().update(str).digest().toHex(); } // ###END###{Certificates} @@ -774,6 +774,7 @@ for (var i = 0; i < 64;) { md5_k[i] = 0 | (Math.abs(Math.sin(++i)) * 4294967296) // Perform MD5 on raw string and return hex function hex_md5(str) { + if (str == null) { str = ''; } var b, c, d, j, x = [], str2 = unescape(encodeURI(str)), diff --git a/public/scripts/amt-wsman-ws-0.2.0.js b/public/scripts/amt-wsman-ws-0.2.0.js index 05c15369..b74a0a08 100644 --- a/public/scripts/amt-wsman-ws-0.2.0.js +++ b/public/scripts/amt-wsman-ws-0.2.0.js @@ -80,12 +80,12 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { // Websocket relay specific private method (Content Length Encoding) obj.sendRequest = function (postdata, url, action) { - url = url ? url : "/wsman"; - action = action ? action : "POST"; - var h = action + " " + url + " HTTP/1.1\r\n"; + url = url ? url : '/wsman'; + action = action ? action : 'POST'; + var h = action + ' ' + url + ' HTTP/1.1\r\n'; if (obj.challengeParams != null) { - var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams["realm"] + ':' + obj.pass) + ':' + obj.challengeParams["nonce"] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams["qop"] + ':' + hex_md5(action + ':' + url)); - h += 'Authorization: ' + obj.renderDigest({ "username": obj.user, "realm": obj.challengeParams["realm"], "nonce": obj.challengeParams["nonce"], "uri": url, "qop": obj.challengeParams["qop"], "response": response, "nc": obj.noncecounter++, "cnonce": obj.cnonce }) + '\r\n'; + var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams['qop'] + ':' + hex_md5(action + ':' + url + ((obj.challengeParams['qop'] == 'auth-int') ? (':' + hex_md5(postdata)) : ''))); + h += 'Authorization: ' + obj.renderDigest({ 'username': obj.user, 'realm': obj.challengeParams['realm'], 'nonce': obj.challengeParams['nonce'], 'uri': url, 'qop': obj.challengeParams['qop'], 'response': response, 'nc': obj.noncecounter++, 'cnonce': obj.cnonce }) + '\r\n'; } //h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding @@ -93,12 +93,11 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { //obj.Debug("SEND: " + h); // Display send packet } - // Websocket relay specific private method - obj.parseDigest = function (header) { - var t = header.substring(7).split(','); - for (i in t) t[i] = t[i].trim(); - return t.reduce(function (obj, s) { var parts = s.split('='); obj[parts[0]] = parts[1].replace(/"/g, ''); return obj; }, {}) - } + // Parse the HTTP digest header and return a list of key & values. + obj.parseDigest = function (header) { return correctedQuoteSplit(header.substring(7)).reduce(function (obj, s) { var parts = s.trim().split('='); obj[parts[0]] = parts[1].replace(new RegExp('\"', 'g'), ''); return obj; }, {}) } + + // Split a string on quotes but do not do it when in quotes + function correctedQuoteSplit(str) { return str.split(',').reduce(function (a, c) { if (a.ic) { a.st[a.st.length - 1] += ',' + c } else { a.st.push(c) } if (c.split('"').length % 2 == 0) { a.ic = !a.ic } return a; }, { st: [], ic: false }).st } // Websocket relay specific private method obj.renderDigest = function (params) { @@ -117,7 +116,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { obj.socketState = 1; console.log(obj.tlsv1only); - obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=1&host=" + obj.host + "&port=" + obj.port + "&tls=" + obj.tls + "&tlsv1only=" + obj.tlsv1only + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "")); // The "p=1" indicates to the relay that this is a WSMAN session + obj.socket = new WebSocket(window.location.protocol.replace('http', 'ws') + '//' + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + '/webrelay.ashx?p=1&host=' + obj.host + '&port=' + obj.port + '&tls=' + obj.tls + '&tlsv1only=' + obj.tlsv1only + ((user == '*') ? '&serverauth=1' : '') + ((typeof pass === 'undefined') ? ('&serverauth=1&user=' + user) : '')); // The "p=1" indicates to the relay that this is a WSMAN session obj.socket.onopen = _OnSocketConnected; obj.socket.onmessage = _OnMessage; obj.socket.onclose = _OnSocketClosed; @@ -154,7 +153,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { fileReader.readAsArrayBuffer(e.data); } else { // IE10, readAsBinaryString does not exist, use an alternative. - var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength; + var binary = '', bytes = new Uint8Array(e.data), length = bytes.byteLength; for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } _OnSocketData(binary); } @@ -169,7 +168,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { if (typeof data === 'object') { // This is an ArrayBuffer, convert it to a string array (used in IE) - var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength; + var binary = '', bytes = new Uint8Array(data), length = bytes.byteLength; for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } data = binary; } @@ -180,10 +179,10 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { obj.socketAccumulator += data; while (true) { if (obj.socketParseState == 0) { - var headersize = obj.socketAccumulator.indexOf("\r\n\r\n"); + var headersize = obj.socketAccumulator.indexOf('\r\n\r\n'); if (headersize < 0) return; //obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header - obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split("\r\n"); + obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n'); obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4); obj.socketParseState = 1; obj.socketData = ''; @@ -197,12 +196,12 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { } if (obj.socketParseState == 1) { var csize = -1; - if ((obj.socketXHeader["connection"] != undefined) && (obj.socketXHeader["connection"].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) { + if ((obj.socketXHeader['connection'] != undefined) && (obj.socketXHeader['connection'].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) { // The body ends with a close, in this case, we will only process the header csize = 0; - } else if (obj.socketXHeader["content-length"] != undefined) { + } else if (obj.socketXHeader['content-length'] != undefined) { // The body length is specified by the content-length - csize = parseInt(obj.socketXHeader["content-length"]); + csize = parseInt(obj.socketXHeader['content-length']); if (obj.socketAccumulator.length < csize) return; var data = obj.socketAccumulator.substring(0, csize); obj.socketAccumulator = obj.socketAccumulator.substring(csize); @@ -239,6 +238,11 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { if (isNaN(s)) s = 602; if (s == 401 && ++(obj.authcounter) < 3) { obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry + if (obj.challengeParams['qop'] != null) { + var qopList = obj.challengeParams['qop'].split(','); + for (var i in qopList) { qopList[i] = qopList[i].trim(); } + if (qopList.indexOf('auth-int') >= 0) { obj.challengeParams['qop'] = 'auth-int'; } else { obj.challengeParams['qop'] = 'auth'; } + } } else { var r = obj.pendingAjaxCall.shift(); // if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work. diff --git a/redirserver.js b/redirserver.js index 720b5e7e..884bda73 100644 --- a/redirserver.js +++ b/redirserver.js @@ -75,7 +75,15 @@ module.exports.CreateRedirServer = function (parent, db, args, func) { parent.letsencrypt.challenge(req.url.slice(leChallengePrefix.length), getCleanHostname(req), function (response) { if (response == null) { res.sendStatus(404); } else { res.send(response); } }); } else { // Everything else - res.set({ 'strict-transport-security': "max-age=60000; includeSubDomains", "Referrer-Policy": "no-referrer", "x-frame-options": "SAMEORIGIN", "X-XSS-Protection": "1; mode=block", "X-Content-Type-Options": "nosniff", "Content-Security-Policy": "default-src http: ws: \"self\" \"unsafe-inline\"" }); + var selfurl = ((args.notls !== true) ? (' wss://' + req.headers.host) : (' ws://' + req.headers.host)); + res.set({ + 'strict-transport-security': 'max-age=60000; includeSubDomains', + 'Referrer-Policy': 'no-referrer', + 'x-frame-options': 'SAMEORIGIN', + 'X-XSS-Protection': '1; mode=block', + 'X-Content-Type-Options': 'nosniff', + 'Content-Security-Policy': "default-src 'none'; style-src 'self' 'unsafe-inline';" + }); return next(); } });