diff --git a/agents/meshcore_diagnostic.js b/agents/meshcore_diagnostic.js index 81d6cb0b09..2c8ec3eea7 100644 --- a/agents/meshcore_diagnostic.js +++ b/agents/meshcore_diagnostic.js @@ -85,9 +85,9 @@ function DownloadAgentBinary(path, ID) { var options = require('http').parseUri(require('MeshAgent').ServerInfo.ServerUri); var downloadUri = 'https://' + options.host + ':' + options.port + '/meshagents?id=' + (ID != null ? ID : getARCHID()); - sendServerLog('Diagnostic: Attempting to downlod agent from: ' + downloadUri); + sendServerLog('Diagnostic: Attempting to download agent from: ' + downloadUri); - return (wget(downloadUri, path, { rejectUnauthorized: false })); + return (wget(downloadUri, path)); } function giveup() @@ -204,3 +204,4 @@ function start() } } }; + diff --git a/agents/modules_meshcmd/amt-wsman.js b/agents/modules_meshcmd/amt-wsman.js index 280c617d72..41e67d906b 100644 --- a/agents/modules_meshcmd/amt-wsman.js +++ b/agents/modules_meshcmd/amt-wsman.js @@ -63,7 +63,7 @@ function WsmanStackCreateService(/*CreateWsmanComm, host, port, user, pass, tls, // Perform a WSMAN Subscribe operation obj.ExecSubscribe = function ExecSubscribe(resuri, delivery, url, callback, tag, pri, selectors, opaque, user, pass) { - var digest = "", digest2 = "", opaque = ""; + var digest = "", digest2 = ""; if (user != null && pass != null) { digest = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken' + user + '' + pass + ''; digest2 = ''; } if (opaque != null) { opaque = '' + opaque + ''; } if (delivery == 'PushWithAck') { delivery = 'dmtf.org/wbem/wsman/1/wsman/PushWithAck'; } else if (delivery == 'Push') { delivery = 'xmlsoap.org/ws/2004/08/eventing/DeliveryModes/Push'; } diff --git a/agents/modules_meshcore/sysinfo.js b/agents/modules_meshcore/sysinfo.js index cc13574dde..23d33824d5 100644 --- a/agents/modules_meshcore/sysinfo.js +++ b/agents/modules_meshcore/sysinfo.js @@ -218,12 +218,13 @@ function macos_memUtilization() mem.MemTotal = (mem.MemFree + mem.MemUsed); mem.percentFree = ((mem.MemFree / mem.MemTotal) * 100);//.toFixed(2); mem.percentConsumed = (((mem.MemTotal - mem.MemFree) / mem.MemTotal) * 100);//.toFixed(2); - return (mem); + ret._res(mem); } else { - throw ('Parse Error'); + ret._rej('Parse Error'); } + return (ret); } function windows_thermals() @@ -243,7 +244,7 @@ function windows_thermals() function linux_thermals() { var ret = []; - child = require('child_process').execFile('/bin/sh', ['sh']); + var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); }); child.stdin.write("for folder in /sys/class/thermal/thermal_zone*/; do [ -e \"$folder/temp\" ] && echo \"$(cat \"$folder/temp\"),$(cat \"$folder/type\")\"; done\nexit\n"); diff --git a/agents/modules_meshcore/wifi-scanner.js b/agents/modules_meshcore/wifi-scanner.js index fa3681d9cf..c805c7918b 100644 --- a/agents/modules_meshcore/wifi-scanner.js +++ b/agents/modules_meshcore/wifi-scanner.js @@ -81,7 +81,7 @@ function WiFiScanner() this.child.ms.on('end', function () { var str = this.buffer.toString(); - tokens = str.split(' - Address: '); + var tokens = str.split(' - Address: '); for (var block in tokens) { if (block == 0) continue; diff --git a/agents/modules_meshcore/win-deskutils.js b/agents/modules_meshcore/win-deskutils.js index 70531a5e11..ad92c6b00c 100644 --- a/agents/modules_meshcore/win-deskutils.js +++ b/agents/modules_meshcore/win-deskutils.js @@ -104,7 +104,7 @@ function sessionDispatch(tsid, parent, method, args) // function background_get(tsid) { - if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null + if (tsid !== undefined) // TSID is not undefined { // Need to disatch to different session first return (sessionDispatch(tsid, 'background', 'get', [])); @@ -123,7 +123,7 @@ function background_get(tsid) // function background_set(path, tsid) { - if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null + if (tsid !== undefined) // TSID is not undefined { // Need to disatch to different session first return (sessionDispatch(tsid, 'background', 'set', [path])); @@ -160,7 +160,7 @@ function dispatch(parent, method, args) // function mousetrails_set(value, tsid) { - if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null + if (tsid !== undefined) // TSID is not undefined { // Need to disatch to different session first return (sessionDispatch(tsid, 'mouse', 'setTrails', [value])); @@ -178,7 +178,7 @@ function mousetrails_set(value, tsid) // function mousetrails_get(tsid) { - if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null + if (tsid !== undefined) // TSID is not undefined { // Need to disatch to different session first return (sessionDispatch(tsid, 'mouse', 'getTrails', [])); @@ -205,7 +205,7 @@ function mousetrails_get(tsid) // function idle_getSeconds(tsid) { - if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null + if (tsid !== undefined) // TSID is not undefined { // Need to dispatch to different session first return (sessionDispatch(tsid, 'idle', 'getSeconds', [])); @@ -296,4 +296,4 @@ function idle_getSecondsAllSessions() module.exports = { background: { get: background_get, set: background_set } }; module.exports.mouse = { getTrails: mousetrails_get, setTrails: mousetrails_set }; module.exports.idle = { getSeconds: idle_getSeconds, getSecondsAllSessions: idle_getSecondsAllSessions }; -module.exports.dispatch = dispatch; \ No newline at end of file +module.exports.dispatch = dispatch; diff --git a/agents/modules_meshcore/win-volumes.js b/agents/modules_meshcore/win-volumes.js index 59e87c1a2e..85b15ee54d 100644 --- a/agents/modules_meshcore/win-volumes.js +++ b/agents/modules_meshcore/win-volumes.js @@ -102,6 +102,7 @@ function windows_volumes() var germanpass = (abc !== '' && abc.includes('Kennwort:') && !abc.includes('Numerisches Kennwort:')); // German Password var frenchpass = (abc !== '' && abc.includes('Mot de passe :') && !abc.includes('Mot de passe num')); // French Password if (englishidpass || germanidpass || frenchidpass|| englishpass || germanpass || frenchpass) { + if (x + 1 >= lines.length) { continue; } var nextline = lines[x + 1].trim(); if (x + 1 < lines.length && (nextline !== '' && (nextline.startsWith('ID:') || nextline.startsWith('ID :')) )) { identifier = nextline.replace('ID:','').replace('ID :', '').trim(); @@ -125,4 +126,4 @@ function windows_volumes() module.exports = { getVolumes: function () { try { return (getVolumes()); } catch (x) { return ({}); } }, volumes_promise: windows_volumes -}; \ No newline at end of file +}; diff --git a/amt/amt-wsman-comm.js b/amt/amt-wsman-comm.js index 25acdba012..661be456aa 100644 --- a/amt/amt-wsman-comm.js +++ b/amt/amt-wsman-comm.js @@ -1,537 +1,537 @@ -/* -Copyright 2020-2021 Intel Corporation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -@description Intel AMT WSMAN communication module for NodeJS -@author Ylian Saint-Hilaire -@version v0.3.0 -*/ - -/*jslint node: true */ -/*jshint node: true */ -/*jshint strict:false */ -/*jshint -W097 */ -/*jshint esversion: 6 */ -"use strict"; - -// Construct a WSMAN stack communication object -var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConnection) { - //console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions); - - var obj = {}; - obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start. - obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls - obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time. - obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent. - obj.challengeParams = null; - obj.noncecounter = 1; - obj.authcounter = 0; - - obj.net = require('net'); - obj.tls = require('tls'); - obj.crypto = require('crypto'); - obj.constants = require('constants'); - obj.socket = null; - obj.socketState = 0; - obj.kerberosDone = 0; - obj.amtVersion = null; - - obj.Address = '/wsman'; - obj.cnonce = obj.crypto.randomBytes(16).toString('hex'); // Generate a random client nonce - - obj.host = host; - obj.port = port; - obj.user = user; - obj.pass = pass; - obj.xtls = tls; - obj.xtlsoptions = tlsoptions; - obj.mpsConnection = mpsConnection; // Link to a MPS connection, this can be CIRA, Relay or LMS. If null, local sockets are used as transport. - obj.xtlsFingerprint; - obj.xtlsCertificate = null; - obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted - obj.xtlsSkipHostCheck = 0; - obj.xtlsMethod = 0; - obj.xtlsDataReceived = false; - obj.digestRealmMatch = null; - obj.digestRealm = null; - - // Private method - obj.Debug = function (msg) { console.log(msg); } - - // Used to add TLS to a steam - function SerialTunnel(options) { - var obj = new require('stream').Duplex(options); - obj.forwardwrite = null; - obj.updateBuffer = function (chunk) { try { this.push(chunk); } catch (ex) { } }; - obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err("Failed to fwd _write."); } if (callback) callback(); }; // Pass data written to forward - obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer() - return obj; - } - - // Private method - // pri = priority, if set to 1, the call is high priority and put on top of the stack. - obj.PerformAjax = function (postdata, callback, tag, pri, url, action) { - if ((obj.ActiveAjaxCount == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && obj.PendingAjax.length == 0) { - // There are no pending AJAX calls, perform the call now. - obj.PerformAjaxEx(postdata, callback, tag, url, action); - } else { - // If this is a high priority call, put this call in front of the array, otherwise put it in the back. - if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); } - } - } - - // Private method - obj.PerformNextAjax = function () { - if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return; - var x = obj.PendingAjax.shift(); - obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]); - obj.PerformNextAjax(); - } - - // Private method - obj.PerformAjaxEx = function (postdata, callback, tag, url, action) { - if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; } - if (!postdata) postdata = ''; - //obj.Debug('SEND: ' + postdata); // DEBUG - - obj.ActiveAjaxCount++; - return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action); - } - - // NODE.js specific private method - obj.pendingAjaxCall = []; - - // NODE.js specific private method - obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 5); } - - // NODE.js specific private method - obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) { - if ((retry <= 0) || (obj.FailAllError != 0)) { - // Too many retry, fail here. - obj.ActiveAjaxCount--; - if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error - obj.PerformNextAjax(); - return; - } - obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]); - if (obj.socketState == 0) { obj.xxConnectHttpSocket(); } - else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); } - } - - // NODE.js specific private method - obj.sendRequest = function (postdata, url, action) { - url = url ? url : '/wsman'; - action = action ? action : 'POST'; - var h = action + ' ' + url + ' HTTP/1.1\r\n'; - if (obj.challengeParams != null) { - obj.digestRealm = obj.challengeParams['realm']; - if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) { - obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error - obj.CancelAllQueries(997); - return; - } - } - if ((obj.user == '*') && (kerberos != null)) { - // Kerberos Auth - if (obj.kerberosDone == 0) { - var ticketName = 'HTTP' + ((obj.tls == 1) ? 'S' : '') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass); - // Ask for the new Kerberos ticket - //console.log('kerberos.getTicket', ticketName); - var ticketReturn = kerberos.getTicket(ticketName); - if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) { - h += 'Authorization: Negotiate ' + ticketReturn.ticket + '\r\n'; - if (process.platform.indexOf('win') >= 0) { - // Clear kerberos tickets on both 32 and 64bit Windows platforms - try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); } - } - } else { - console.log('Unexpected Kerberos error code: ' + ticketReturn.returnCode); - } - obj.kerberosDone = 1; - } - } else if (obj.challengeParams != null) { - var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + nonceHex(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': nonceHex(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 - obj.xxSend(h); - //console.log('SEND: ' + h); // Display send packet - } - - // 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 } - function nonceHex(v) { var s = ('00000000' + v.toString(16)); return s.substring(s.length - 8); } - - // NODE.js specific private method - obj.renderDigest = function (params) { - var paramsnames = []; - for (var i in params) { paramsnames.push(i); } - return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + (((ii == 'nc') || (ii == 'qop')) ? (ii + '=' + params[ii]) : (ii + '="' + params[ii] + '"')); }, '').substring(1); - } - - // NODE.js specific private method - obj.xxConnectHttpSocket = function () { - //obj.Debug("xxConnectHttpSocket"); - obj.socketParseState = 0; - obj.socketAccumulator = ''; - obj.socketHeader = null; - obj.socketData = ''; - obj.socketState = 1; - obj.kerberosDone = 0; - - if (obj.mpsConnection != null) { - if (obj.xtls != 1) { - // Setup a new channel using the CIRA/Relay/LMS connection - obj.socket = obj.mpsConnection.SetupChannel(obj.port); - if (obj.socket == null) { obj.xxOnSocketClosed(); return; } - - // Connect without TLS - obj.socket.onData = function (ccon, data) { obj.xxOnSocketData(data); } - obj.socket.onStateChange = function (ccon, state) { - if (state == 0) { - // Channel closed - obj.socketParseState = 0; - obj.socketAccumulator = ''; - obj.socketHeader = null; - obj.socketData = ''; - obj.socketState = 0; - obj.xxOnSocketClosed(); - } else if (state == 2) { - // Channel open success - obj.xxOnSocketConnected(); - } - } - } else { - // Setup a new channel using the CIRA/Relay/LMS connection - obj.cirasocket = obj.mpsConnection.SetupChannel(obj.port); - if (obj.cirasocket == null) { obj.xxOnSocketClosed(); return; } - - // Connect with TLS - var ser = new SerialTunnel(); - - // let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl) - // Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write - ser.forwardwrite = function (msg) { try { obj.cirasocket.write(msg); } catch (ex) { } }; // TLS ---> CIRA - - // When APF tunnel return something, update SerialTunnel buffer - obj.cirasocket.onData = function (ciraconn, data) { if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } } }; // CIRA ---> TLS - - // Handle CIRA tunnel state change - obj.cirasocket.onStateChange = function (ciraconn, state) { - if (state == 0) { obj.xxOnSocketClosed(); } - if (state == 2) { - // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF - var options = { socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false }; - if (obj.xtlsMethod == 1) { - options.secureProtocol = 'TLSv1_method'; - } else { - options.minVersion = 'TLSv1'; - } - if (obj.xtlsoptions) { - if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; } - if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; } - if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; } - } - - obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected); - obj.socket.setEncoding('binary'); - obj.socket.setTimeout(60000); // Set socket idle timeout - obj.socket.on('error', function (ex) { obj.xtlsMethod = 1 - obj.xtlsMethod; }); - obj.socket.on('close', obj.xxOnSocketClosed); - obj.socket.on('timeout', obj.destroy); - - // Decrypted tunnel from TLS communcation to be forwarded to websocket - obj.socket.on('data', function (data) { try { obj.xxOnSocketData(data.toString('binary')); } catch (e) { } }); // AMT/TLS ---> WS - - // If TLS is on, forward it through TLSSocket - obj.forwardclient = obj.socket; - obj.forwardclient.xtls = 1; - } - }; - } - } else { - // Direct connection - if (obj.xtls != 1) { - // Direct connect without TLS - obj.socket = new obj.net.Socket(); - obj.socket.setEncoding('binary'); - obj.socket.setTimeout(60000); // Set socket idle timeout - obj.socket.on('data', obj.xxOnSocketData); - obj.socket.on('close', obj.xxOnSocketClosed); - obj.socket.on('timeout', obj.destroy); - obj.socket.on('error', obj.xxOnSocketClosed); - obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected); - } else { - // Direct connect with TLS - var options = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false }; - if (obj.xtlsMethod == 1) { - options.secureProtocol = 'TLSv1_method'; - } else { - options.minVersion = 'TLSv1'; - } - if (obj.xtlsoptions) { - if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; } - if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; } - if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; } - } - obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected); - obj.socket.setEncoding('binary'); - obj.socket.setTimeout(28000); // Set socket idle timeout of 28 seconds - obj.socket.on('data', obj.xxOnSocketData); - obj.socket.on('close', obj.xxOnSocketClosed); - obj.socket.on('timeout', obj.destroy); - obj.socket.on('error', function (ex) { if (ex.message && ex.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } }); - } - obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining? - } - } - - // Get the certificate of Intel AMT - obj.getPeerCertificate = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate(); } return null; } - obj.getPeerCertificateFingerprint = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate().fingerprint.split(':').join('').toLowerCase(); } return null; } - - // Check if the certificate matched the certificate hash. - function checkCertHash(cert, hash) { - // Check not required - if (hash == 0) return true; - - // SHA1 compare - if (cert.fingerprint.split(':').join('').toLowerCase() == hash) return true; - - // SHA256 compare - if ((hash.length == 64) && (obj.crypto.createHash('sha256').update(cert.raw).digest('hex') == hash)) { return true; } - - // SHA384 compare - if ((hash.length == 96) && (obj.crypto.createHash('sha384').update(cert.raw).digest('hex') == hash)) { return true; } - - return false; - } - - // NODE.js specific private method - obj.xxOnSocketConnected = function () { - if (obj.socket == null) return; - // check TLS certificate for webrelay and direct only - if (obj.xtls == 1) { - obj.xtlsCertificate = obj.socket.getPeerCertificate(); - - // Setup the forge certificate check - var camatch = 0; - if ((obj.xtlsoptions != null) && (obj.xtlsoptions.ca != null)) { - var forgeCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(atob(obj.xtlsCertificate.raw.toString('base64')))); - var caStore = forge.pki.createCaStore(obj.xtlsoptions.ca); - // Got thru all certificates in the store and look for a match. - for (var i in caStore.certs) { - if (camatch == 0) { - var c = caStore.certs[i], verified = false; - try { verified = c.verify(forgeCert); } catch (e) { } - if (verified == true) { camatch = c; } - } - } - // We found a match, check that the CommonName matches the hostname - if ((obj.xtlsSkipHostCheck == 0) && (camatch != 0)) { - amtcertname = forgeCert.subject.getField('CN').value; - if (amtcertname.toLowerCase() != obj.host.toLowerCase()) { camatch = 0; } - } - } - if ((camatch == 0) && (checkCertHash(obj.xtlsCertificate, obj.xtlsFingerprint) == false)) { - obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error - obj.CancelAllQueries(998); - return; - } - if ((obj.xtlsFingerprint == 0) && (camatch == 0)) { obj.xtlsCheck = 3; } else { obj.xtlsCheck = (camatch == 0) ? 2 : 1; } - } else { obj.xtlsCheck = 0; } - obj.socketState = 2; - obj.socketParseState = 0; - for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); } - } - - // NODE.js specific private method - obj.xxOnSocketData = function (data) { - //console.log('RECV: ' + data); - obj.xtlsDataReceived = true; - 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; - for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } - data = binary; - } - else if (typeof data !== 'string') return; - - obj.socketAccumulator += data; - while (true) { - //console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator); - if (obj.socketParseState == 0) { - var headersize = obj.socketAccumulator.indexOf('\r\n\r\n'); - if (headersize < 0) return; - //obj.Debug("Header: "+obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header - obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n'); - if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } } - obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4); - obj.socketParseState = 1; - obj.socketData = ''; - obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') }; - for (i in obj.socketHeader) { - if (i != 0) { - var x2 = obj.socketHeader[i].indexOf(':'); - obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2); - } - } - } - 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'))) { - // The body ends with a close, in this case, we will only process the header - csize = 0; - } else if (obj.socketXHeader['content-length'] != undefined) { - // The body length is specified by the 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); - obj.socketData = data; - csize = 0; - } else { - // The body is chunked - var clen = obj.socketAccumulator.indexOf('\r\n'); - if (clen < 0) return; // Chunk length not found, exit now and get more data. - // Chunk length if found, lets see if we can get the data. - csize = parseInt(obj.socketAccumulator.substring(0, clen), 16); - if (obj.socketAccumulator.length < clen + 2 + csize + 2) return; - // We got a chunk with all of the data, handle the chunck now. - var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize); - obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2); - try { obj.socketData += data; } catch (ex) { console.log(ex, typeof data, data.length); } - } - if (csize == 0) { - //obj.Debug("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData); - obj.xxProcessHttpResponse(obj.socketXHeader, obj.socketData); - obj.socketParseState = 0; - obj.socketHeader = null; - } - } - } - } - - // NODE.js specific private method - obj.xxProcessHttpResponse = function (header, data) { - //obj.Debug("xxProcessHttpResponse: " + header.Directive[1]); - - var s = parseInt(header.Directive[1]); - if (isNaN(s)) s = 500; - 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'; } - } - if (obj.mpsConnection == null) { obj.socket.end(); } else { obj.socket.close(); } - } else { - var r = obj.pendingAjaxCall.shift(); - if ((r == null) || (r.length < 1)) { /*console.log("pendingAjaxCall error, " + r);*/ return; } // Get a response without any pending requests. - //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. - obj.authcounter = 0; - obj.ActiveAjaxCount--; - obj.gotNextMessages(data, 'success', { status: s }, r); - obj.PerformNextAjax(); - } - } - - // NODE.js specific private method - obj.xxOnSocketClosed = function () { - //obj.Debug("xxOnSocketClosed"); - obj.socketState = 0; - if (obj.socket != null) { - if (obj.socket.removeAllListeners) { - // Do not remove the error handler since it may still get triggered. - obj.socket.removeAllListeners('data'); - obj.socket.removeAllListeners('close'); - obj.socket.removeAllListeners('timeout'); - } - try { - if (obj.mpsConnection == null) { - obj.socket.destroy(); - } else { - if (obj.cirasocket != null) { obj.cirasocket.close(); } else { obj.socket.close(); } - } - } catch (ex) { } - obj.socket = null; - obj.cirasocket = null; - } - if (obj.pendingAjaxCall.length > 0) { - var r = obj.pendingAjaxCall.shift(), retry = r[5]; - setTimeout(function () { obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry) }, 500); // Wait half a second and try again - } - } - - obj.destroy = function () { - if (obj.socket != null) { - if (obj.socket.removeAllListeners) { - // Do not remove the error handler since it may still get triggered. - obj.socket.removeAllListeners('data'); - obj.socket.removeAllListeners('close'); - obj.socket.removeAllListeners('timeout'); - } - try { - if (obj.mpsConnection == null) { - obj.socket.destroy(); - } else { - if (obj.cirasocket != null) { obj.cirasocket.close(); } else { obj.socket.close(); } - } - } catch (ex) { } - delete obj.socket; - delete obj.cirasocket; - obj.socketState = 0; - } - } - - // NODE.js specific private method - obj.xxSend = function (x) { - //console.log('xxSend', x); - if (obj.socketState == 2) { obj.socket.write(Buffer.from(x, 'binary')); } - } - - // Cancel all pending queries with given status - obj.CancelAllQueries = function (s) { - obj.FailAllError = s; - while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); } - obj.destroy(); - } - - // Private method - obj.gotNextMessages = function (data, status, request, callArgs) { - if (obj.FailAllError == 999) return; - if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; } - if (request.status != 200) { try { callArgs[1](null, request.status, callArgs[2]); } catch (ex) { console.error(ex); } return; } - try { callArgs[1](data, 200, callArgs[2]); } catch (ex) { console.error(ex); } - } - - // Private method - obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) { - if (obj.FailAllError == 999) return; - if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; } - try { callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]); } catch (ex) { console.error(ex); } - } - - // MD5 digest hash - function hex_md5(str) { return obj.crypto.createHash('md5').update(str).digest('hex'); } - - return obj; -} - -module.exports = CreateWsmanComm; \ No newline at end of file +/* +Copyright 2020-2021 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +@description Intel AMT WSMAN communication module for NodeJS +@author Ylian Saint-Hilaire +@version v0.3.0 +*/ + +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; + +// Construct a WSMAN stack communication object +var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConnection) { + //console.log('CreateWsmanComm', host, port, user, pass, tls, tlsoptions); + + var obj = {}; + obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start. + obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls + obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time. + obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent. + obj.challengeParams = null; + obj.noncecounter = 1; + obj.authcounter = 0; + + obj.net = require('net'); + obj.tls = require('tls'); + obj.crypto = require('crypto'); + obj.constants = require('constants'); + obj.socket = null; + obj.socketState = 0; + obj.kerberosDone = 0; + obj.amtVersion = null; + + obj.Address = '/wsman'; + obj.cnonce = obj.crypto.randomBytes(16).toString('hex'); // Generate a random client nonce + + obj.host = host; + obj.port = port; + obj.user = user; + obj.pass = pass; + obj.xtls = tls; + obj.xtlsoptions = tlsoptions; + obj.mpsConnection = mpsConnection; // Link to a MPS connection, this can be CIRA, Relay or LMS. If null, local sockets are used as transport. + obj.xtlsFingerprint; + obj.xtlsCertificate = null; + obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted + obj.xtlsSkipHostCheck = 0; + obj.xtlsMethod = 0; + obj.xtlsDataReceived = false; + obj.digestRealmMatch = null; + obj.digestRealm = null; + + // Private method + obj.Debug = function (msg) { console.log(msg); } + + // Used to add TLS to a steam + function SerialTunnel(options) { + var obj = new require('stream').Duplex(options); + obj.forwardwrite = null; + obj.updateBuffer = function (chunk) { try { this.push(chunk); } catch (ex) { } }; + obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.error("Failed to fwd _write."); } if (callback) callback(); }; // Pass data written to forward + obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer() + return obj; + } + + // Private method + // pri = priority, if set to 1, the call is high priority and put on top of the stack. + obj.PerformAjax = function (postdata, callback, tag, pri, url, action) { + if ((obj.ActiveAjaxCount == 0 || ((obj.ActiveAjaxCount < obj.MaxActiveAjaxCount) && (obj.challengeParams != null))) && obj.PendingAjax.length == 0) { + // There are no pending AJAX calls, perform the call now. + obj.PerformAjaxEx(postdata, callback, tag, url, action); + } else { + // If this is a high priority call, put this call in front of the array, otherwise put it in the back. + if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); } + } + } + + // Private method + obj.PerformNextAjax = function () { + if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return; + var x = obj.PendingAjax.shift(); + obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]); + obj.PerformNextAjax(); + } + + // Private method + obj.PerformAjaxEx = function (postdata, callback, tag, url, action) { + if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; } + if (!postdata) postdata = ''; + //obj.Debug('SEND: ' + postdata); // DEBUG + + obj.ActiveAjaxCount++; + return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action); + } + + // NODE.js specific private method + obj.pendingAjaxCall = []; + + // NODE.js specific private method + obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 5); } + + // NODE.js specific private method + obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) { + if ((retry <= 0) || (obj.FailAllError != 0)) { + // Too many retry, fail here. + obj.ActiveAjaxCount--; + if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error + obj.PerformNextAjax(); + return; + } + obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]); + if (obj.socketState == 0) { obj.xxConnectHttpSocket(); } + else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); } + } + + // NODE.js specific private method + obj.sendRequest = function (postdata, url, action) { + url = url ? url : '/wsman'; + action = action ? action : 'POST'; + var h = action + ' ' + url + ' HTTP/1.1\r\n'; + if (obj.challengeParams != null) { + obj.digestRealm = obj.challengeParams['realm']; + if (obj.digestRealmMatch && (obj.digestRealm != obj.digestRealmMatch)) { + obj.FailAllError = 997; // Cause all new responses to be silent. 997 = Digest Realm check error + obj.CancelAllQueries(997); + return; + } + } + if ((obj.user == '*') && (kerberos != null)) { + // Kerberos Auth + if (obj.kerberosDone == 0) { + var ticketName = 'HTTP' + ((obj.tls == 1) ? 'S' : '') + '/' + ((obj.pass == '') ? (obj.host + ':' + obj.port) : obj.pass); + // Ask for the new Kerberos ticket + //console.log('kerberos.getTicket', ticketName); + var ticketReturn = kerberos.getTicket(ticketName); + if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) { + h += 'Authorization: Negotiate ' + ticketReturn.ticket + '\r\n'; + if (process.platform.indexOf('win') >= 0) { + // Clear kerberos tickets on both 32 and 64bit Windows platforms + try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); } + } + } else { + console.log('Unexpected Kerberos error code: ' + ticketReturn.returnCode); + } + obj.kerberosDone = 1; + } + } else if (obj.challengeParams != null) { + var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams['realm'] + ':' + obj.pass) + ':' + obj.challengeParams['nonce'] + ':' + nonceHex(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': nonceHex(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 + obj.xxSend(h); + //console.log('SEND: ' + h); // Display send packet + } + + // 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 } + function nonceHex(v) { var s = ('00000000' + v.toString(16)); return s.substring(s.length - 8); } + + // NODE.js specific private method + obj.renderDigest = function (params) { + var paramsnames = []; + for (var i in params) { paramsnames.push(i); } + return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + (((ii == 'nc') || (ii == 'qop')) ? (ii + '=' + params[ii]) : (ii + '="' + params[ii] + '"')); }, '').substring(1); + } + + // NODE.js specific private method + obj.xxConnectHttpSocket = function () { + //obj.Debug("xxConnectHttpSocket"); + obj.socketParseState = 0; + obj.socketAccumulator = ''; + obj.socketHeader = null; + obj.socketData = ''; + obj.socketState = 1; + obj.kerberosDone = 0; + + if (obj.mpsConnection != null) { + if (obj.xtls != 1) { + // Setup a new channel using the CIRA/Relay/LMS connection + obj.socket = obj.mpsConnection.SetupChannel(obj.port); + if (obj.socket == null) { obj.xxOnSocketClosed(); return; } + + // Connect without TLS + obj.socket.onData = function (ccon, data) { obj.xxOnSocketData(data); } + obj.socket.onStateChange = function (ccon, state) { + if (state == 0) { + // Channel closed + obj.socketParseState = 0; + obj.socketAccumulator = ''; + obj.socketHeader = null; + obj.socketData = ''; + obj.socketState = 0; + obj.xxOnSocketClosed(); + } else if (state == 2) { + // Channel open success + obj.xxOnSocketConnected(); + } + } + } else { + // Setup a new channel using the CIRA/Relay/LMS connection + obj.cirasocket = obj.mpsConnection.SetupChannel(obj.port); + if (obj.cirasocket == null) { obj.xxOnSocketClosed(); return; } + + // Connect with TLS + var ser = new SerialTunnel(); + + // let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl) + // Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write + ser.forwardwrite = function (msg) { try { obj.cirasocket.write(msg); } catch (ex) { } }; // TLS ---> CIRA + + // When APF tunnel return something, update SerialTunnel buffer + obj.cirasocket.onData = function (ciraconn, data) { if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } } }; // CIRA ---> TLS + + // Handle CIRA tunnel state change + obj.cirasocket.onStateChange = function (ciraconn, state) { + if (state == 0) { obj.xxOnSocketClosed(); } + if (state == 2) { + // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF + var options = { socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false }; + if (obj.xtlsMethod == 1) { + options.secureProtocol = 'TLSv1_method'; + } else { + options.minVersion = 'TLSv1'; + } + if (obj.xtlsoptions) { + if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; } + if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; } + if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; } + } + + obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected); + obj.socket.setEncoding('binary'); + obj.socket.setTimeout(60000); // Set socket idle timeout + obj.socket.on('error', function (ex) { obj.xtlsMethod = 1 - obj.xtlsMethod; }); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('timeout', obj.destroy); + + // Decrypted tunnel from TLS communcation to be forwarded to websocket + obj.socket.on('data', function (data) { try { obj.xxOnSocketData(data.toString('binary')); } catch (e) { } }); // AMT/TLS ---> WS + + // If TLS is on, forward it through TLSSocket + obj.forwardclient = obj.socket; + obj.forwardclient.xtls = 1; + } + }; + } + } else { + // Direct connection + if (obj.xtls != 1) { + // Direct connect without TLS + obj.socket = new obj.net.Socket(); + obj.socket.setEncoding('binary'); + obj.socket.setTimeout(60000); // Set socket idle timeout + obj.socket.on('data', obj.xxOnSocketData); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('timeout', obj.destroy); + obj.socket.on('error', obj.xxOnSocketClosed); + obj.socket.connect(obj.port, obj.host, obj.xxOnSocketConnected); + } else { + // Direct connect with TLS + var options = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE | obj.constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false }; + if (obj.xtlsMethod == 1) { + options.secureProtocol = 'TLSv1_method'; + } else { + options.minVersion = 'TLSv1'; + } + if (obj.xtlsoptions) { + if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; } + if (obj.xtlsoptions.cert) { options.cert = obj.xtlsoptions.cert; } + if (obj.xtlsoptions.key) { options.key = obj.xtlsoptions.key; } + } + obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected); + obj.socket.setEncoding('binary'); + obj.socket.setTimeout(28000); // Set socket idle timeout of 28 seconds + obj.socket.on('data', obj.xxOnSocketData); + obj.socket.on('close', obj.xxOnSocketClosed); + obj.socket.on('timeout', obj.destroy); + obj.socket.on('error', function (ex) { if (ex.message && ex.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } }); + } + obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining? + } + } + + // Get the certificate of Intel AMT + obj.getPeerCertificate = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate(); } return null; } + obj.getPeerCertificateFingerprint = function () { if (obj.xtls == 1) { return obj.socket.getPeerCertificate().fingerprint.split(':').join('').toLowerCase(); } return null; } + + // Check if the certificate matched the certificate hash. + function checkCertHash(cert, hash) { + // Check not required + if (hash == 0) return true; + + // SHA1 compare + if (cert.fingerprint.split(':').join('').toLowerCase() == hash) return true; + + // SHA256 compare + if ((hash.length == 64) && (obj.crypto.createHash('sha256').update(cert.raw).digest('hex') == hash)) { return true; } + + // SHA384 compare + if ((hash.length == 96) && (obj.crypto.createHash('sha384').update(cert.raw).digest('hex') == hash)) { return true; } + + return false; + } + + // NODE.js specific private method + obj.xxOnSocketConnected = function () { + if (obj.socket == null) return; + // check TLS certificate for webrelay and direct only + if (obj.xtls == 1) { + obj.xtlsCertificate = obj.socket.getPeerCertificate(); + + // Setup the forge certificate check + var camatch = 0; + if ((obj.xtlsoptions != null) && (obj.xtlsoptions.ca != null)) { + var forgeCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(atob(obj.xtlsCertificate.raw.toString('base64')))); + var caStore = forge.pki.createCaStore(obj.xtlsoptions.ca); + // Got thru all certificates in the store and look for a match. + for (var i in caStore.certs) { + if (camatch == 0) { + var c = caStore.certs[i], verified = false; + try { verified = c.verify(forgeCert); } catch (e) { } + if (verified == true) { camatch = c; } + } + } + // We found a match, check that the CommonName matches the hostname + if ((obj.xtlsSkipHostCheck == 0) && (camatch != 0)) { + amtcertname = forgeCert.subject.getField('CN').value; + if (amtcertname.toLowerCase() != obj.host.toLowerCase()) { camatch = 0; } + } + } + if ((camatch == 0) && (checkCertHash(obj.xtlsCertificate, obj.xtlsFingerprint) == false)) { + obj.FailAllError = 998; // Cause all new responses to be silent. 998 = TLS Certificate check error + obj.CancelAllQueries(998); + return; + } + if ((obj.xtlsFingerprint == 0) && (camatch == 0)) { obj.xtlsCheck = 3; } else { obj.xtlsCheck = (camatch == 0) ? 2 : 1; } + } else { obj.xtlsCheck = 0; } + obj.socketState = 2; + obj.socketParseState = 0; + for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); } + } + + // NODE.js specific private method + obj.xxOnSocketData = function (data) { + //console.log('RECV: ' + data); + obj.xtlsDataReceived = true; + 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; + for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } + data = binary; + } + else if (typeof data !== 'string') return; + + obj.socketAccumulator += data; + while (true) { + //console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator); + if (obj.socketParseState == 0) { + var headersize = obj.socketAccumulator.indexOf('\r\n\r\n'); + if (headersize < 0) return; + //obj.Debug("Header: "+obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header + obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n'); + if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } } + obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4); + obj.socketParseState = 1; + obj.socketData = ''; + obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') }; + for (i in obj.socketHeader) { + if (i != 0) { + var x2 = obj.socketHeader[i].indexOf(':'); + obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2); + } + } + } + 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'))) { + // The body ends with a close, in this case, we will only process the header + csize = 0; + } else if (obj.socketXHeader['content-length'] != undefined) { + // The body length is specified by the 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); + obj.socketData = data; + csize = 0; + } else { + // The body is chunked + var clen = obj.socketAccumulator.indexOf('\r\n'); + if (clen < 0) return; // Chunk length not found, exit now and get more data. + // Chunk length if found, lets see if we can get the data. + csize = parseInt(obj.socketAccumulator.substring(0, clen), 16); + if (obj.socketAccumulator.length < clen + 2 + csize + 2) return; + // We got a chunk with all of the data, handle the chunck now. + var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize); + obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2); + try { obj.socketData += data; } catch (ex) { console.log(ex, typeof data, data.length); } + } + if (csize == 0) { + //obj.Debug("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData); + obj.xxProcessHttpResponse(obj.socketXHeader, obj.socketData); + obj.socketParseState = 0; + obj.socketHeader = null; + } + } + } + } + + // NODE.js specific private method + obj.xxProcessHttpResponse = function (header, data) { + //obj.Debug("xxProcessHttpResponse: " + header.Directive[1]); + + var s = parseInt(header.Directive[1]); + if (isNaN(s)) s = 500; + 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'; } + } + if (obj.mpsConnection == null) { obj.socket.end(); } else { obj.socket.close(); } + } else { + var r = obj.pendingAjaxCall.shift(); + if ((r == null) || (r.length < 1)) { /*console.log("pendingAjaxCall error, " + r);*/ return; } // Get a response without any pending requests. + //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. + obj.authcounter = 0; + obj.ActiveAjaxCount--; + obj.gotNextMessages(data, 'success', { status: s }, r); + obj.PerformNextAjax(); + } + } + + // NODE.js specific private method + obj.xxOnSocketClosed = function () { + //obj.Debug("xxOnSocketClosed"); + obj.socketState = 0; + if (obj.socket != null) { + if (obj.socket.removeAllListeners) { + // Do not remove the error handler since it may still get triggered. + obj.socket.removeAllListeners('data'); + obj.socket.removeAllListeners('close'); + obj.socket.removeAllListeners('timeout'); + } + try { + if (obj.mpsConnection == null) { + obj.socket.destroy(); + } else { + if (obj.cirasocket != null) { obj.cirasocket.close(); } else { obj.socket.close(); } + } + } catch (ex) { } + obj.socket = null; + obj.cirasocket = null; + } + if (obj.pendingAjaxCall.length > 0) { + var r = obj.pendingAjaxCall.shift(), retry = r[5]; + setTimeout(function () { obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry) }, 500); // Wait half a second and try again + } + } + + obj.destroy = function () { + if (obj.socket != null) { + if (obj.socket.removeAllListeners) { + // Do not remove the error handler since it may still get triggered. + obj.socket.removeAllListeners('data'); + obj.socket.removeAllListeners('close'); + obj.socket.removeAllListeners('timeout'); + } + try { + if (obj.mpsConnection == null) { + obj.socket.destroy(); + } else { + if (obj.cirasocket != null) { obj.cirasocket.close(); } else { obj.socket.close(); } + } + } catch (ex) { } + delete obj.socket; + delete obj.cirasocket; + obj.socketState = 0; + } + } + + // NODE.js specific private method + obj.xxSend = function (x) { + //console.log('xxSend', x); + if (obj.socketState == 2) { obj.socket.write(Buffer.from(x, 'binary')); } + } + + // Cancel all pending queries with given status + obj.CancelAllQueries = function (s) { + obj.FailAllError = s; + while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); } + obj.destroy(); + } + + // Private method + obj.gotNextMessages = function (data, status, request, callArgs) { + if (obj.FailAllError == 999) return; + if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; } + if (request.status != 200) { try { callArgs[1](null, request.status, callArgs[2]); } catch (ex) { console.error(ex); } return; } + try { callArgs[1](data, 200, callArgs[2]); } catch (ex) { console.error(ex); } + } + + // Private method + obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) { + if (obj.FailAllError == 999) return; + if (obj.FailAllError != 0) { try { callArgs[1](null, obj.FailAllError, callArgs[2]); } catch (ex) { console.error(ex); } return; } + try { callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]); } catch (ex) { console.error(ex); } + } + + // MD5 digest hash + function hex_md5(str) { return obj.crypto.createHash('md5').update(str).digest('hex'); } + + return obj; +} + +module.exports = CreateWsmanComm; diff --git a/amtprovisioningserver.js b/amtprovisioningserver.js index d31b7b8387..47da4e3f35 100644 --- a/amtprovisioningserver.js +++ b/amtprovisioningserver.js @@ -39,7 +39,7 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) { socket.on('error', function (err) { }) socket.on('close', function () { if (this.data != null) { processHelloData(this.data, this.ra); } delete this.ra; this.removeAllListeners(); }) socket.on('data', function (data) { - if (this.data == null) { this.data = data; } else { Buffer.concat([this.data, data]); } + if (this.data == null) { this.data = data; } else { this.data = Buffer.concat([this.data, data]); } var str = this.data.toString(); if (str.startsWith('GET ') && (str.indexOf('\r\n\r\n') >= 0)) { this.data = null; @@ -696,3 +696,4 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) { return obj; }; + diff --git a/interceptor.js b/interceptor.js index c6e4a87820..dafe85eb25 100644 --- a/interceptor.js +++ b/interceptor.js @@ -86,7 +86,7 @@ module.exports.CreateHttpInterceptor = function (args) { } else if (obj.amt.mode == 1) { // Length Body Mode // Send the body of content-length size var rl = obj.amt.count; - if (rl < obj.amt.acc.length) rl = obj.amt.acc.length; + if (rl > obj.amt.acc.length) rl = obj.amt.acc.length; r = obj.amt.acc.substring(0, rl); obj.amt.acc = obj.amt.acc.substring(rl); obj.amt.count -= rl; @@ -197,38 +197,38 @@ module.exports.CreateHttpInterceptor = function (args) { } else if (obj.ws.mode == 1) { // Length Body Mode // Send the body of content-length size var rl = obj.ws.count; - if (rl < obj.ws.acc.length) rl = obj.ws.acc.length; + if (rl > obj.ws.acc.length) rl = obj.ws.acc.length; r = obj.ws.acc.substring(0, rl); obj.ws.acc = obj.ws.acc.substring(rl); obj.ws.count -= rl; if (obj.ws.count == 0) { obj.ws.mode = 0; } return r; - } else if (obj.amt.mode == 2) { // Chunked Body Mode + } else if (obj.ws.mode == 2) { // Chunked Body Mode // Send data one chunk at a time - headerend = obj.amt.acc.indexOf('\r\n'); + headerend = obj.ws.acc.indexOf('\r\n'); if (headerend < 0) return ''; - var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16); + var chunksize = parseInt(obj.ws.acc.substring(0, headerend), 16); if (isNaN(chunksize)) { // TODO: Check this path // Chunk is not in this batch, move one - r = obj.amt.acc.substring(0, headerend + 2); - obj.amt.acc = obj.amt.acc.substring(headerend + 2); + r = obj.ws.acc.substring(0, headerend + 2); + obj.ws.acc = obj.ws.acc.substring(headerend + 2); // Peek if we next is the end of chunked transfer - headerend = obj.amt.acc.indexOf('\r\n'); + headerend = obj.ws.acc.indexOf('\r\n'); if (headerend > 0) { - chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16); - if (chunksize == 0) { obj.amt.mode = 0; } + chunksize = parseInt(obj.ws.acc.substring(0, headerend), 16); + if (chunksize == 0) { obj.ws.mode = 0; } } return r; - } else if (chunksize == 0 && obj.amt.acc.length >= headerend + 4) { + } else if (chunksize == 0 && obj.ws.acc.length >= headerend + 4) { // Send the ending chunk (NOTE: We do not support trailing headers) - r = obj.amt.acc.substring(0, headerend + 4); - obj.amt.acc = obj.amt.acc.substring(headerend + 4); - obj.amt.mode = 0; + r = obj.ws.acc.substring(0, headerend + 4); + obj.ws.acc = obj.ws.acc.substring(headerend + 4); + obj.ws.mode = 0; return r; - } else if (chunksize > 0 && obj.amt.acc.length >= headerend + 4) { + } else if (chunksize > 0 && obj.ws.acc.length >= headerend + 4) { // Send a chunk - r = obj.amt.acc.substring(0, headerend + chunksize + 4); - obj.amt.acc = obj.amt.acc.substring(headerend + chunksize + 4); + r = obj.ws.acc.substring(0, headerend + chunksize + 4); + obj.ws.acc = obj.ws.acc.substring(headerend + chunksize + 4); return r; } } else if (obj.ws.mode == 3) { // Until Close Mode @@ -457,4 +457,4 @@ module.exports.CreateRedirInterceptor = function (args) { }; return obj; -}; \ No newline at end of file +}; diff --git a/mcrec.js b/mcrec.js index 6761227807..be632218e4 100644 --- a/mcrec.js +++ b/mcrec.js @@ -233,7 +233,7 @@ function readLastBlock(state, func) { var xtype = buf2.readUInt16BE(0); // Type (1 = Header, 2 = Network Data, 3 = End, 4 = Extra Metadata) var xflags = buf2.readUInt16BE(2); // Flags (1 = Binary, 2 = User) var xsize = buf2.readUInt32BE(4); // Size - var xtime = buf.readUIntBE(10, 6); // Time + var xtime = buf2.readUIntBE(10, 6); // Time var buf3 = Buffer.alloc(xsize); fs.read(state.recFile, buf3, 0, xsize, time + 16, function (err, bytesRead, buf3) { func(state, true, xtime, JSON.parse(buf3.toString())); @@ -331,4 +331,4 @@ if (directRun) { setup(); } // Export table module.exports.startEx = startEx; -module.exports.indexFile = indexFile; \ No newline at end of file +module.exports.indexFile = indexFile; diff --git a/monitoring.js b/monitoring.js index c73e688b9a..84c8b9d72a 100644 --- a/monitoring.js +++ b/monitoring.js @@ -40,7 +40,7 @@ module.exports.CreateMonitoring = function (parent, args) { ConnectedUsers: { description: "Connected Users" }, // Object.keys(parent.webserver.wssessions).length UsersSessions: { description: "Users Sessions" }, // Object.keys(parent.webserver.wssessions2).length RelaySessions: { description: "Relay Sessions" }, // parent.webserver.relaySessionCount - RelayCount: { description: "Relay Count" } // Object.keys(parent.webserver.wsrelays).length30bb4fb74dfb758d36be52a7 + RelayCount: { description: "Relay Count" } // Object.keys(parent.webserver.wsrelays).length } obj.collectors = []; if (parent.config.settings.prometheus != null) { // Create Prometheus Monitoring Endpoint @@ -119,4 +119,4 @@ module.exports.CreateMonitoring = function (parent, args) { } } return obj; -} \ No newline at end of file +} diff --git a/pkcs7-modified.js b/pkcs7-modified.js index 661904a6bb..5f36b2248d 100644 --- a/pkcs7-modified.js +++ b/pkcs7-modified.js @@ -27,7 +27,7 @@ try { require('../node-forge/lib/pkcs7asn1'); require('../node-forge/lib/random'); require('../node-forge/lib/util'); - require('../node-forge/lib/x509'); f + require('../node-forge/lib/x509'); } catch (ex) { } if (forge == null) { @@ -1277,3 +1277,4 @@ function _decryptContent(msg) { msg.content = ciph.output; } } + diff --git a/public/js/ui-components.js b/public/js/ui-components.js index 7bc6c88fc7..8645704aa0 100644 --- a/public/js/ui-components.js +++ b/public/js/ui-components.js @@ -32,24 +32,6 @@ class ModernModal { const sizeClass = this.options.size === 'large' ? 'modal-lg' : this.options.size === 'extra-large' ? 'modal-xl' : ''; - let modalContent = ` - - `; - setModalContent(this.modalId, title, content, this.options.size); if (okCallback) { @@ -393,3 +375,4 @@ if (typeof module !== 'undefined' && module.exports) { createIconUploadComponent }; } + diff --git a/public/novnc/core/ra2.js b/public/novnc/core/ra2.js index d330b848d2..cf6523e825 100644 --- a/public/novnc/core/ra2.js +++ b/public/novnc/core/ra2.js @@ -10,7 +10,7 @@ class RA2Cipher { async setKey(key) { this._cipher = await legacyCrypto.importKey( - "raw", key, { name: "AES-EAX" }, false, ["encrypt, decrypt"]); + "raw", key, { name: "AES-EAX" }, false, ["encrypt", "decrypt"]); } async makeMessage(message) { diff --git a/public/scripts/agent-redir-rtc-0.1.0-min.js b/public/scripts/agent-redir-rtc-0.1.0-min.js index e08bad4f13..bdca523441 100644 --- a/public/scripts/agent-redir-rtc-0.1.0-min.js +++ b/public/scripts/agent-redir-rtc-0.1.0-min.js @@ -1 +1 @@ -var CreateKvmDataChannel=function(e,t,n){var a={};a.m=t,t.parent=a,a.webchannel=e,a.State=0,a.protocol=t.protocol,a.onStateChanged=null,a.onControlMsg=null,a.debugmode=0,a.keepalive=n,a.rtcKeepAlive=null,a.Start=function(){1==a.debugmode&&console.log("start"),a.xxStateChange(3),a.webchannel.onmessage=a.xxOnMessage,a.rtcKeepAlive=setInterval(a.xxSendRtcKeepAlive,3e4)};var r=new FileReader,o=!1,l=[];return r.readAsBinaryString?r.onload=function(e){a.xxOnSocketData(e.target.result),0==l.length?o=!1:r.readAsBinaryString(new Blob([l.shift()]))}:r.readAsArrayBuffer&&(r.onloadend=function(e){a.xxOnSocketData(e.target.result),0==l.length?o=!1:r.readAsArrayBuffer(l.shift())}),a.xxOnMessage=function(e){if("string"!=typeof e.data)if("object"==typeof e.data){if(1==o)return void l.push(e.data);if(r.readAsBinaryString)o=!0,r.readAsBinaryString(new Blob([e.data]));else if(f.readAsArrayBuffer)o=!0,r.readAsArrayBuffer(e.data);else{for(var t="",n=new Uint8Array(e.data),i=n.byteLength,s=0;s 30) { nextTagNumber = new type.UInt8().read(s).value; } @@ -109,3 +110,4 @@ module.exports = { decode : decode, encode : encode }; + diff --git a/rdp/core/log.js b/rdp/core/log.js index c2eb6a8e88..b1e826c2b4 100644 --- a/rdp/core/log.js +++ b/rdp/core/log.js @@ -67,19 +67,15 @@ module.exports = { level: Levels.INFO, // Levels.INFO, Levels: Levels, debug: function (message) { - //console.log(message); - //logger.debug(message); + log('DEBUG', message); }, info: function (message) { - //console.log(message); - //logger.info(message); + log('INFO', message); }, warn: function (message) { - //console.log(message); - //logger.warn(message); + log('WARN', message); }, error: function (message) { - //console.log(message); - //logger.error(message); + log('ERROR', message); } }; diff --git a/rdp/protocol/cert.js b/rdp/protocol/cert.js index 9f9ecc6331..0c80a80f05 100644 --- a/rdp/protocol/cert.js +++ b/rdp/protocol/cert.js @@ -24,7 +24,7 @@ var rsa = require('../security').rsa; var asn1 = require('../asn1'); /** - *  @see http://msdn.microsoft.com/en-us/library/cc240521.aspx + * @see http://msdn.microsoft.com/en-us/library/cc240521.aspx */ var CertificateType = { CERT_CHAIN_VERSION_1 : 0x00000001, @@ -158,7 +158,7 @@ function certificate() { self.certData = x509CertificateChain().read(s); break; default: - log.error('unknown cert type ' + self.dwVersion.value & 0x7fffffff); + log.error('unknown cert type ' + (self.dwVersion.value & 0x7fffffff)); } }) }; @@ -172,4 +172,4 @@ function certificate() { module.exports = { CertificateType : CertificateType, certificate : certificate -}; \ No newline at end of file +}; diff --git a/rdp/protocol/pdu/data.js b/rdp/protocol/pdu/data.js index dec3ffd8f2..66957e286f 100644 --- a/rdp/protocol/pdu/data.js +++ b/rdp/protocol/pdu/data.js @@ -128,16 +128,6 @@ var UpdateType = { UPDATETYPE_SYNCHRONIZE : 0x0003 }; -/** - * @see http://msdn.microsoft.com/en-us/library/cc240608.aspx - */ -var UpdateType = { - UPDATETYPE_ORDERS : 0x0000, - UPDATETYPE_BITMAP : 0x0001, - UPDATETYPE_PALETTE : 0x0002, - UPDATETYPE_SYNCHRONIZE : 0x0003 -}; - /** * @see http://msdn.microsoft.com/en-us/library/cc240583.aspx */ @@ -704,7 +694,7 @@ function clientInputEventPDU(inputs, opt) { return self.slowPathInputEvents.obj.length; }), pad2Octets : new type.UInt16Le(), - slowPathInputEvents : inputs || new type.Factory(function(s) { + slowPathInputEvents : inputs || new type.Factory(function(s) { self.slowPathInputEvents = new type.Component([]); for(var i = 0; i < self.numEvents.value; i++) { self.slowPathInputEvents.obj.push(slowPathInputEvent().read(s)); @@ -871,7 +861,7 @@ function bitmapUpdateDataPDU(data, opt) { numberRectangles : new type.UInt16Le(function() { return self.rectangles.obj.length; }), - rectangles : data || new type.Factory(function(s) { + rectangles : data || new type.Factory(function(s) { self.rectangles = new type.Component([]); for(var i = 0; i < self.numberRectangles.value; i++) { self.rectangles.obj.push(bitmapData().read(s)); @@ -943,7 +933,7 @@ function dataPDU(pduData, shareId, opt) { }), function() { return self.pduData.obj.__PDUTYPE2__; }, shareId), - pduData : pduData || new type.Factory(function(s) { + pduData : pduData || new type.Factory(function(s) { //compute local readLength var options = { readLength : new type.CallableValue(function() { @@ -1164,7 +1154,7 @@ function fastPathUpdatePDU(updateData, opt) { size : new type.UInt16Le( function () { return self.updateData.size(); }), - updateData : updateData || new type.Factory( function (s) { + updateData : updateData || new type.Factory( function (s) { var options = { readLength : new type.CallableValue( function () { return self.size.value; }) }; @@ -1249,4 +1239,4 @@ module.exports = { fastPathUpdatePDU: fastPathUpdatePDU, clipPDU: clipPDU, ClipPDUMsgType: ClipPDUMsgType -}; \ No newline at end of file +}; diff --git a/rdp/protocol/pdu/global.js b/rdp/protocol/pdu/global.js index e6a978334a..274bfe18ba 100644 --- a/rdp/protocol/pdu/global.js +++ b/rdp/protocol/pdu/global.js @@ -99,7 +99,7 @@ function Client(transport, fastPathTransport) { this.clientCapabilities[caps.CapsType.CAPSTYPE_OFFSCREENCACHE] = caps.offscreenBitmapCacheCapability(); this.clientCapabilities[caps.CapsType.CAPSTYPE_VIRTUALCHANNEL] = caps.virtualChannelCapability(); this.clientCapabilities[caps.CapsType.CAPSTYPE_SOUND] = caps.soundCapability(); - this.clientCapabilities[caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate(); + this.clientCapabilities[caps.CapsETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate(); } // inherit from Layer @@ -205,6 +205,7 @@ Client.prototype.recvServerControlCooperatePDU = function(s) { this.transport.once('data', function(s) { self.recvServerControlCooperatePDU(s); }); + return; } var self = this; @@ -229,6 +230,7 @@ Client.prototype.recvServerControlGrantedPDU = function(s) { this.transport.once('data', function(s) { self.recvServerControlGrantedPDU(s); }); + return; } var self = this; @@ -252,6 +254,7 @@ Client.prototype.recvServerFontMapPDU = function(s) { this.transport.once('data', function(s) { self.recvServerFontMapPDU(s); }); + return; } this.emit('connect'); @@ -404,4 +407,4 @@ Client.prototype.sendInputEvents = function (inputEvents) { */ module.exports = { Client : Client -}; \ No newline at end of file +}; diff --git a/rdp/protocol/t125/gcc.js b/rdp/protocol/t125/gcc.js index 42a17a1c8d..adb258a911 100644 --- a/rdp/protocol/t125/gcc.js +++ b/rdp/protocol/t125/gcc.js @@ -444,8 +444,8 @@ function readConferenceCreateResponse(s) { throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY'); } - length = per.readLength(s); - serverSettings = settings(null, { readLength : new type.CallableValue(length) }); + var length = per.readLength(s); + var serverSettings = settings(null, { readLength : new type.CallableValue(length) }); // Object magic return serverSettings.read(s).obj.blocks.obj.map(function(e) { @@ -479,7 +479,7 @@ function readConferenceCreateRequest (s) { per.readOctetStream(s, h221_cs_key, 4); - length = per.readLength(s); + var length = per.readLength(s); var clientSettings = settings(null, { readLength : new type.CallableValue(length) }); // Object magic @@ -537,4 +537,4 @@ module.exports = { readConferenceCreateRequest : readConferenceCreateRequest, writeConferenceCreateRequest : writeConferenceCreateRequest, writeConferenceCreateResponse : writeConferenceCreateResponse -}; \ No newline at end of file +}; diff --git a/rdp/protocol/t125/per.js b/rdp/protocol/t125/per.js index 17cbf46639..3507757483 100644 --- a/rdp/protocol/t125/per.js +++ b/rdp/protocol/t125/per.js @@ -204,7 +204,7 @@ function readObjectIdentifier(s, oid) { * @returns {type.Component} per encoded object identifier */ function writeObjectIdentifier(oid) { - return new type.Component([new type.UInt8(5), new type.UInt8((oid[0] << 4) & (oid[1] & 0x0f)), new type.UInt8(oid[2]), new type.UInt8(oid[3]), new type.UInt8(oid[4]), new type.UInt8(oid[5])]); + return new type.Component([new type.UInt8(5), new type.UInt8((oid[0] << 4) | (oid[1] & 0x0f)), new type.UInt8(oid[2]), new type.UInt8(oid[3]), new type.UInt8(oid[4]), new type.UInt8(oid[5])]); } /** @@ -335,4 +335,4 @@ module.exports = { writePadding : writePadding, readOctetStream : readOctetStream, writeOctetStream : writeOctetStream -}; \ No newline at end of file +}; diff --git a/rdp/protocol/x224.js b/rdp/protocol/x224.js index c323b7dbab..fcd373983c 100644 --- a/rdp/protocol/x224.js +++ b/rdp/protocol/x224.js @@ -43,6 +43,19 @@ var NegotiationType = { TYPE_RDP_NEG_FAILURE : 0x03 }; +/** + * Failure codes used in negotiation failure packet + * @see http://msdn.microsoft.com/en-us/library/cc240507.aspx + */ +var NegotiationFailureCode = { + SSL_REQUIRED_BY_SERVER : 0x00000001, + SSL_NOT_ALLOWED_BY_SERVER : 0x00000002, + SSL_CERT_NOT_ON_SERVER : 0x00000003, + INCONSISTENT_FLAGS : 0x00000004, + HYBRID_REQUIRED_BY_SERVER : 0x00000005, + SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER : 0x00000006 +}; + /** * Protocols available for x224 layer */ @@ -308,7 +321,7 @@ Server.prototype.recvConnectionRequest = function (s) { if (!(this.selectedProtocol & Protocols.PROTOCOL_SSL)) { var confirm = serverConnectionConfirm(); - confirm.obj.protocolNeg.obj.type.value = NegociationType.TYPE_RDP_NEG_FAILURE; + confirm.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_FAILURE; confirm.obj.protocolNeg.obj.result.value = NegotiationFailureCode.SSL_REQUIRED_BY_SERVER; this.transport.send(confirm); this.close(); @@ -347,3 +360,4 @@ module.exports = { Client : Client, Server : Server }; + diff --git a/rdp/security/rc4.js b/rdp/security/rc4.js index 3b116fe185..5d445e01cf 100644 --- a/rdp/security/rc4.js +++ b/rdp/security/rc4.js @@ -63,8 +63,8 @@ RC4.prototype.encrypt = function (input) { RC4.prototype.decrypt = function (input) { var outputText = ''; input = input.match(/[a-z0-9]{2}/gi); - for (var i = 0, ii = input.length; i < ii; i++) { outputText += String.fromCharCode((parseInt(input[i], 16) ^ byteStream.next().value)); } + for (var i = 0, ii = input.length; i < ii; i++) { outputText += String.fromCharCode((parseInt(input[i], 16) ^ this.byteStream.next().value)); } return outputText; } -module.exports = RC4; \ No newline at end of file +module.exports = RC4;