diff --git a/.github/workflows/changes.yaml b/.github/workflows/changes.yaml index b18710462b..679bb3217a 100644 --- a/.github/workflows/changes.yaml +++ b/.github/workflows/changes.yaml @@ -67,6 +67,21 @@ jobs: "helm": ["./charts/**"] }' + # any top-level directory not already covered above is tracked + # generically here so new components are not silently ignored + EXTRA_DIRS='[]' + while IFS= read -r dir; do + EXTRA_DIRS=$(jq -c --arg d "$dir" '. + [$d]' <<< "$EXTRA_DIRS") + done < <( + find . -maxdepth 1 -mindepth 1 -type d \ + ! -name '.git' ! -name '.github' ! -name 'docker' ! -name 'charts' \ + -printf '%f\n' | sort + ) + + ADDONS=$(jq -nc --argjson addons "$ADDONS" --argjson extra "$EXTRA_DIRS" ' + $addons + ( [ $extra[] | { key: ., value: ["./\(.)/**"] } ] | from_entries ) + ') + images_matrix=$(jq -c '[ .[] | { name, path: (.path // "./docker"), @@ -109,3 +124,4 @@ jobs: reduce $keys[] as $k ({}; .[$k] = ((($changed | index($k)) != null) | tostring)) ') echo "changes=$changes" >> "$GITHUB_OUTPUT" + diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 1071a831a0..f5a1ca0a29 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -37,6 +37,14 @@ jobs: exit 0 fi + if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then + git fetch origin "$BRANCH" + if ! git merge-base --is-ancestor "origin/$BRANCH" origin/master; then + echo "::notice::$BRANCH has diverged from origin/master with manual commits — skipping to avoid overwriting them" + exit 0 + fi + fi + git checkout -B "$BRANCH" origin/master if ! git merge --no-ff --no-edit upstream/master; then echo "::notice::merge has conflicts — skipping, will retry next run" @@ -47,3 +55,4 @@ jobs: gh pr create --repo "$GITHUB_REPOSITORY" --base master --head "$BRANCH" \ --title "Sync from Fork" \ --body "Automatic weekly sync from \`Ylianst/MeshCentral@master\`." + diff --git a/agents/hashagents.js b/agents/hashagents.js index 5ad8a26b57..48b9aca63f 100644 --- a/agents/hashagents.js +++ b/agents/hashagents.js @@ -1,4 +1,5 @@ var fs = require('fs'); +var crypto = require('crypto'); var agents = { 'MeshService.exe': 3, @@ -30,6 +31,13 @@ var agents = { 'meshagent_osx-universal-64': 10005 } +function getSHA384FileHash(filename) { + var hash = crypto.createHash('sha384'); + var data = fs.readFileSync(filename); + hash.update(data); + return hash.digest(); +} + function getAgentInfo(filename, id) { if (fs.existsSync(filename) != true) return null; var stats = fs.statSync(filename); @@ -42,4 +50,4 @@ for (var i in agents) { if (info != null) { agentinfo[agents[i]] = info; } } console.log(JSON.stringify(agentinfo, null, 2)); -process.exit(); \ No newline at end of file +process.exit(); diff --git a/agents/meshinstall-linux.sh b/agents/meshinstall-linux.sh index eeb7799d50..57ff5b5ec1 100644 --- a/agents/meshinstall-linux.sh +++ b/agents/meshinstall-linux.sh @@ -144,13 +144,13 @@ DownloadAgent() { meshid=$3 machineid=$4 echo "Downloading agent #$machineid..." - wget $url/meshagents?id=$machineid {{{wgetoptionshttps}}}-O ./meshagent || curl {{{curloptionshttps}}}--output ./meshagent $url/meshagents?id=$machineid + wget "$url/meshagents?id=$machineid" {{{wgetoptionshttps}}}-O ./meshagent || curl {{{curloptionshttps}}}--output ./meshagent "$url/meshagents?id=$machineid" # If it did not work, try again using http if [ $? != 0 ] then url=${url/"https://"/"http://"} - wget $url/meshagents?id=$machineid {{{wgetoptionshttp}}}-O ./meshagent || curl {{{curloptionshttp}}}--output ./meshagent $url/meshagents?id=$machineid + wget "$url/meshagents?id=$machineid" {{{wgetoptionshttp}}}-O ./meshagent || curl {{{curloptionshttp}}}--output ./meshagent "$url/meshagents?id=$machineid" fi if [ $? -eq 0 ] @@ -158,12 +158,12 @@ DownloadAgent() { echo "Agent downloaded." # TODO: We could check the meshagent sha256 hash, but best to authenticate the server. chmod 755 ./meshagent - wget $url/meshsettings?id=$meshid {{{wgetoptionshttps}}}-O ./meshagent.msh || curl {{{curloptionshttps}}}--output ./meshagent.msh $url/meshsettings?id=$meshid + wget "$url/meshsettings?id=$meshid" {{{wgetoptionshttps}}}-O ./meshagent.msh || curl {{{curloptionshttps}}}--output ./meshagent.msh "$url/meshsettings?id=$meshid" # If it did not work, try again using http if [ $? -ne 0 ] then - wget $url/meshsettings?id=$meshid {{{wgetoptionshttp}}}-O ./meshagent.msh || curl {{{curloptionshttp}}}--output ./meshagent.msh $url/meshsettings?id=$meshid + wget "$url/meshsettings?id=$meshid" {{{wgetoptionshttp}}}-O ./meshagent.msh || curl {{{curloptionshttp}}}--output ./meshagent.msh "$url/meshsettings?id=$meshid" fi if [ $? -eq 0 ] @@ -208,3 +208,4 @@ then else echo "Must be root to install or uninstall the agent." fi + diff --git a/agents/modules_meshcmd/amt-redir-duk.js b/agents/modules_meshcmd/amt-redir-duk.js index 5bbcaf763d..116744c812 100644 --- a/agents/modules_meshcmd/amt-redir-duk.js +++ b/agents/modules_meshcmd/amt-redir-duk.js @@ -4,6 +4,8 @@ * @version v0.0.1f */ +'use strict'; + // Construct a MeshServer object module.exports = function CreateAmtRedirect(module) { var obj = {}; @@ -123,7 +125,7 @@ module.exports = function CreateAmtRedirect(module) { var status = obj.amtaccumulator[1]; var authType = obj.amtaccumulator[4]; var authData = []; - for (i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator[9 + i]); } + for (var i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator[9 + i]); } var authDataBuf = obj.amtaccumulator.slice(9, 9 + authDataLen); cmdsize = 9 + authDataLen; if (authType == 0) { @@ -229,7 +231,7 @@ module.exports = function CreateAmtRedirect(module) { obj.connectstate = 1; obj.m.Start(); // KVM traffic, forward rest of accumulator directly. - if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); } + if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.slice(8)); } cmdsize = obj.amtaccumulator.length; break; default: @@ -306,4 +308,4 @@ function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); } var md5hasher = require('MD5Stream').create(); -function hex_md5(a) { return md5hasher.syncHash(a).toString('hex').toLowerCase(); } \ No newline at end of file +function hex_md5(a) { return md5hasher.syncHash(a).toString('hex').toLowerCase(); } diff --git a/agents/modules_meshcmd/amt-xml.js b/agents/modules_meshcmd/amt-xml.js index cf82c02c98..bbd4fa7308 100644 --- a/agents/modules_meshcmd/amt-xml.js +++ b/agents/modules_meshcmd/amt-xml.js @@ -70,9 +70,15 @@ function _ParseWsmanRec(node) { return r; } +function _GetNameFromUrl(url) { + if (!url) return url; + var i = url.lastIndexOf('/'); + return (i >= 0) ? url.substring(i + 1) : url; +} + function _PutObjToBodyXml(resuri, putObj) { if (!resuri || putObj == null) return ''; - var objname = obj.GetNameFromUrl(resuri); + var objname = _GetNameFromUrl(resuri); var result = ''; for (var prop in putObj) { @@ -187,3 +193,4 @@ function _turnToXmlRec(text) { } catch (ex) { return null; } return lastElement; } + diff --git a/agents/modules_meshcore/monitor-border.js b/agents/modules_meshcore/monitor-border.js index efe5248ac2..020c1c4fdb 100644 --- a/agents/modules_meshcore/monitor-border.js +++ b/agents/modules_meshcore/monitor-border.js @@ -44,6 +44,7 @@ function windows_monitorborder() this.Stop = function Stop() { + clearInterval(info.redInterval); info.redInterval = null; var drawRect = gm.CreateVariable(16); @@ -144,6 +145,7 @@ function linux_monitorborder() this.Stop = function Stop() { + clearTimeout(this._timeout); this._timeout = null; if(!this._isUnity) { @@ -333,3 +335,4 @@ switch(process.platform) + diff --git a/agents/modules_meshcore/sysinfo.js b/agents/modules_meshcore/sysinfo.js index cc13574dde..b164287717 100644 --- a/agents/modules_meshcore/sysinfo.js +++ b/agents/modules_meshcore/sysinfo.js @@ -125,10 +125,12 @@ function linux_cpuUtilization() { for (y = x; y < columns.length; ++y) { currSum += parseInt(columns[y]); } currIdle = parseInt(columns[3 + x]); - var diffIdle = currIdle - cpuLastIdle[cpuNo]; - var diffSum = currSum - cpuLastSum[cpuNo]; + var isFirstSample = (cpuLastIdle[cpuNo] === undefined || cpuLastSum[cpuNo] === undefined); - utilization = (100 - ((diffIdle / diffSum) * 100)); + var diffIdle = isFirstSample ? 0 : (currIdle - cpuLastIdle[cpuNo]); + var diffSum = isFirstSample ? 0 : (currSum - cpuLastSum[cpuNo]); + + utilization = isFirstSample ? 0 : (100 - ((diffIdle / diffSum) * 100)); cpuLastSum[cpuNo] = currSum; cpuLastIdle[cpuNo] = currIdle; @@ -222,7 +224,7 @@ function macos_memUtilization() } else { - throw ('Parse Error'); + throw (new Error('Parse Error')); } } diff --git a/agents/modules_meshcore/wifi-scanner.js b/agents/modules_meshcore/wifi-scanner.js index fa3681d9cf..f5c4e91aea 100644 --- a/agents/modules_meshcore/wifi-scanner.js +++ b/agents/modules_meshcore/wifi-scanner.js @@ -74,14 +74,15 @@ function WiFiScanner() { this.child = require('child_process').execFile('/sbin/iwlist', ['iwlist', wlan, 'scan']); this.child.parent = this; + this.child.str = ''; this.child.ms = new MemoryStream(); this.child.ms.parent = this.child; - this.child.stdout.on('data', function (buffer) { this.parent.ms.write(buffer); }); + this.child.stdout.on('data', function (buffer) { this.parent.str += buffer.toString(); this.parent.ms.write(buffer); }); this.child.on('exit', function () { this.ms.end(); }); this.child.ms.on('end', function () { - var str = this.buffer.toString(); - tokens = str.split(' - Address: '); + var str = this.parent.str; + var tokens = str.split(' - Address: '); for (var block in tokens) { if (block == 0) continue; @@ -125,3 +126,4 @@ module.exports = WiFiScanner; + diff --git a/crowdsec.js b/crowdsec.js index 04e506dbac..8372c1c8d5 100644 --- a/crowdsec.js +++ b/crowdsec.js @@ -4,7 +4,6 @@ module.exports.CreateCrowdSecBouncer = function (parent, config) { // Setup constants const { getLogger } = require('@crowdsec/express-bouncer/src/nodejs-bouncer/lib/logger'); const { configure, renderBanWall, testConnectionToCrowdSec, getRemediationForIp } = require('@crowdsec/express-bouncer/src/nodejs-bouncer'); - const applyCaptcha = require('@crowdsec/express-bouncer/src/express-crowdsec-middleware/lib/captcha'); const { BYPASS_REMEDIATION, CAPTCHA_REMEDIATION, BAN_REMEDIATION } = require('@crowdsec/express-bouncer/src/nodejs-bouncer/lib/constants'); // "bypass", "captcha", "ban"; const svgCaptcha = require('svg-captcha'); const { renderCaptchaWall } = require('@crowdsec/express-bouncer/src/nodejs-bouncer'); @@ -118,6 +117,10 @@ module.exports.CreateCrowdSecBouncer = function (parent, config) { } } + if (currentCaptchaIpList[ip] == null) { + generateCaptcha(ip, captchaGenerationCacheDuration); + } + const captchaWallTemplate = await renderCaptchaWall({ captchaImageTag: currentCaptchaIpList[ip].data, captchaResolutionFormUrl: '', error }); res.status(401); res.send(captchaWallTemplate); diff --git a/mcrec.js b/mcrec.js index 6761227807..58f789ce7f 100644 --- a/mcrec.js +++ b/mcrec.js @@ -227,13 +227,12 @@ function readLastBlock(state, func) { var magic = buf.toString('utf8', 16, 32); if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCNDX')) { // Extra metadata present, lets read it. - extraMetadata = null; var buf2 = Buffer.alloc(16); fs.read(state.recFile, buf2, 0, 16, time, function (err, bytesRead, buf2) { 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 +330,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/meshmessaging.js b/meshmessaging.js index 9a722792a0..044adebd63 100644 --- a/meshmessaging.js +++ b/meshmessaging.js @@ -295,14 +295,17 @@ module.exports.CreateServer = function (parent) { // Example: findUserByTab('aaaa#0000', function (userid) { sendMsg(userid, 'message'); }); async function discordFindUserByTag(userTag, func) { var username = userTag.split('#')[0]; + var found = false; const guilds = await obj.discordClient.guilds.fetch(); - guilds.forEach(async function (value, key) { + for (const [key, value] of guilds) { var guild = await value.fetch(); const guildMembers = await guild.members.search({ query: username }); - guildMembers.forEach(async function (value, key) { - if ((value.user.username + (value.user.discriminator != '0' ? '#' + value.user.discriminator : ''))== userTag) { func(key); return; } - }); - }); + for (const [memberKey, memberValue] of guildMembers) { + if ((memberValue.user.username + (memberValue.user.discriminator != '0' ? '#' + memberValue.user.discriminator : '')) == userTag) { found = true; func(memberKey); break; } + } + if (found) break; + } + if (!found) { func(null); } } // Send an XMPP message @@ -323,6 +326,11 @@ module.exports.CreateServer = function (parent) { sendTelegramMessage(to, msg, func); } else if ((to.startsWith('discord:')) && (obj.discordClient != null)) { // Discord discordFindUserByTag(to.substring(8), function (userid) { + if (userid == null) { + parent.debug('email', 'Discord user not found: ' + to.substring(8)); + if (func != null) { func(false, 'Discord user not found.'); } + return; + } parent.debug('email', 'Sending Discord message to: ' + to.substring(9) + ', ' + userid + ': ' + msg); discordSendMsg(userid, msg); if (func != null) { func(true); } }); @@ -773,3 +781,4 @@ module.exports.SetupTelegram = async function (parent) { console.log('Telegram seems to be configured correctly in the config.json, no need to run --setuptelegram.'); process.exit(); }; + diff --git a/meshscanner.js b/meshscanner.js index 5eb630af13..610d495e93 100644 --- a/meshscanner.js +++ b/meshscanner.js @@ -166,7 +166,7 @@ module.exports.CreateMeshScanner = function (parent) { if ((typeof obj.parent.config.domains[''].title2 == 'string') && (obj.parent.config.domains[''].title2.length > 0)) { info = obj.common.replacePlaceholders(obj.parent.config.domains[''].title2, { 'serverversion': obj.parent.currentVer, - 'servername': obj.getWebServerName(domain, req), + 'servername': parent.certificates.CommonName, 'agentsessions': Object.keys(parent.webserver.wsagents).length, 'connectedusers': Object.keys(parent.webserver.wssessions).length, 'userssessions': Object.keys(parent.webserver.wssessions2).length, @@ -245,7 +245,7 @@ module.exports.CreateMeshScanner = function (parent) { function sendPendingPacket() { if (obj.pendingOutboundPackets.length == 0) { if (obj.pendingOutboundTimer != null) { clearInterval(obj.pendingOutboundTimer); obj.pendingOutboundTimer = null; } return; } var packet = obj.pendingOutboundPackets.shift(); - if (packet != null) { packet[0].send(packet[1], 0, packet[1].length, packet[2], packet[3]); } + if (packet != null) { try { packet[0].send(packet[1], 0, packet[1].length, packet[2], packet[3]); } catch (e) { } } } // As a side job, we also send server wake-on-lan packets @@ -276,4 +276,4 @@ module.exports.CreateMeshScanner = function (parent) { }; return obj; -}; \ No newline at end of file +}; diff --git a/monitoring.js b/monitoring.js index c73e688b9a..d4ae74f937 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 @@ -76,10 +76,10 @@ module.exports.CreateMonitoring = function (parent, args) { UsersSessions: Object.keys(parent.webserver.wssessions2).length, RelaySessions: parent.webserver.relaySessionCount, RelayCount: Object.keys(parent.webserver.wsrelays).length, - ConnectedIntelAMT: 0 + ConnectedIntelAMT: 0, + ConnectedIntelAMTCira: 0 }; if (parent.mpsserver != null) { - gauges.ConnectedIntelAMTCira = 0; for (var i in parent.mpsserver.ciraConnections) { gauges.ConnectedIntelAMTCira += parent.mpsserver.ciraConnections[i].length; } @@ -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..47af0f5676 100644 --- a/public/js/ui-components.js +++ b/public/js/ui-components.js @@ -29,38 +29,17 @@ class ModernModal { } show(title, content, okCallback = null, okButtonText = 'OK') { - 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) { - showModal(this.modalId, `${this.modalId}OkBtn`, okCallback); + showModal(`${this.modalId}Modal`, `${this.modalId}OkBtn`, okCallback); } else { - showModal(this.modalId); + showModal(`${this.modalId}Modal`); } } hide() { - const modalElement = document.getElementById(this.modalId); + const modalElement = document.getElementById(`${this.modalId}Modal`); if (modalElement) { const modal = bootstrap.Modal.getInstance(modalElement); if (modal) { @@ -246,6 +225,12 @@ class IconUploadComponent { window.iconUploadComponents[this.iconKey] = this; } + destroy() { + if (window.iconUploadComponents && (window.iconUploadComponents[this.iconKey] === this)) { + delete window.iconUploadComponents[this.iconKey]; + } + } + triggerFileUpload() { const fileInput = document.getElementById(`iconFile_${this.iconKey}`); if (fileInput) { diff --git a/public/novnc/core/ra2.js b/public/novnc/core/ra2.js index d330b848d2..69bfebb82a 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) { @@ -310,3 +310,4 @@ export default class RSAAESAuthenticationState extends EventTargetMixin { this._hasStarted = s; } } + diff --git a/public/samples/relay.js b/public/samples/relay.js index 6b1f4f65f2..5169dfba77 100644 --- a/public/samples/relay.js +++ b/public/samples/relay.js @@ -19,9 +19,9 @@ var createMeshConnection = function (connectionId) { obj.websocket.onopen = function (e) { console.log('WebSocket Connected', e); }; obj.websocket.onmessage = function (e) { console.log('WebSocket Message', e); - if ((obj.state = 1) && (e.data == 'c')) { + if ((obj.state == 1) && (e.data == 'c')) { obj.state = 2; - if (obj.onStateChanged) { onStateChanged(obj, 2); } + if (obj.onStateChanged) { obj.onStateChanged(obj, 2); } console.log('WebSocket Peer Connection', e); obj.send('bob'); } else { @@ -31,11 +31,11 @@ var createMeshConnection = function (connectionId) { obj.websocket.onclose = function (e) { console.log('WebSocket Closed', e); obj.state = 0; - if (obj.onStateChanged) { onStateChanged(obj, 0); } + if (obj.onStateChanged) { obj.onStateChanged(obj, 0); } }; obj.websocket.onerror = function (e) { console.log('WebSocket Error', e); }; obj.state = 1; - if (obj.onStateChanged) { onStateChanged(obj, 1); } + if (obj.onStateChanged) { obj.onStateChanged(obj, 1); } } return obj; }; @@ -45,4 +45,4 @@ var createMeshConnection = function (connectionId) { }; return obj; -} \ No newline at end of file +} diff --git a/public/scripts/agent-rdp-0.0.1.js b/public/scripts/agent-rdp-0.0.1.js index affa3e4574..9ec0c62687 100644 --- a/public/scripts/agent-rdp-0.0.1.js +++ b/public/scripts/agent-rdp-0.0.1.js @@ -45,7 +45,10 @@ var CreateRDPDesktop = function (canvasid, domainUrl) { obj.socket.binaryType = 'arraybuffer'; obj.socket.onopen = function () { changeState(2); // Setup state - obj.socket.send(JSON.stringify(['infos', { + // NOTE: credentials are sent once, over TLS (wss://), directly to the relay that + // establishes the RDP session. Avoid logging this payload; do not enable debug + // flags that dump raw SEND/RECV payloads in environments handling real credentials. + var infosPayload = ['infos', { ip: obj.nodeid, port: obj.port, screen: { width: obj.width, height: obj.height }, @@ -54,7 +57,8 @@ var CreateRDPDesktop = function (canvasid, domainUrl) { password: credentials.password, options: options, locale: Mstsc.locale() - }])); + }]; + obj.socket.send(JSON.stringify(infosPayload)); }; obj.socket.onmessage = function (evt) { if (typeof evt.data == 'string') { @@ -297,3 +301,4 @@ var CreateRDPDesktop = function (canvasid, domainUrl) { return obj; } + diff --git a/public/scripts/amt-wsman-0.2.0.js b/public/scripts/amt-wsman-0.2.0.js index 0519288341..66ed3e126a 100644 --- a/public/scripts/amt-wsman-0.2.0.js +++ b/public/scripts/amt-wsman-0.2.0.js @@ -124,7 +124,7 @@ var WsmanStackCreateService = function (host, port, user, pass, tls, extra) { } return r; } catch (e) { - console.log("Unable to parse XML: " + xml); + console.log("Unable to parse XML: [error suppressed to avoid leaking sensitive payload data]"); return null; } } @@ -250,3 +250,4 @@ var WsmanStackCreateService = function (host, port, user, pass, tls, extra) { return obj; } + diff --git a/public/scripts/amt-wsman-ws-0.2.0.js b/public/scripts/amt-wsman-ws-0.2.0.js index b428c38813..56ffe2e142 100644 --- a/public/scripts/amt-wsman-ws-0.2.0.js +++ b/public/scripts/amt-wsman-ws-0.2.0.js @@ -105,7 +105,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { // Websocket relay specific private method obj.renderDigest = function (params) { var paramsnames = []; - for (i in params) { paramsnames.push(i); } + 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); } @@ -128,7 +128,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { function _OnSocketConnected() { //obj.Debug("xxOnSocketConnected"); obj.socketState = 2; - for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); } + for (var i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); } } // Websocket relay specific private method @@ -146,7 +146,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls) { obj.socketParseState = 1; obj.socketData = ''; obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') }; - for (i in obj.socketHeader) { + for (var 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); diff --git a/rdp/core/type.js b/rdp/core/type.js index 3303b3eb3b..3f26195a13 100644 --- a/rdp/core/type.js +++ b/rdp/core/type.js @@ -303,15 +303,15 @@ inherits(SingleType, Type); * @param s */ SingleType.prototype.writeValue = function(s) { - var safeValue = this.value; - if (typeof safeValue === 'number') { // FIX: Sanitize coordinates to prevent crashes - safeValue = Math.round(safeValue); // Round to nearest integer (fixes -1.01 issues) - if (safeValue < 0) safeValue = 0; // Clamp to 0 if negative - // nbBytes is 1 (UInt8), 2 (UInt16), or 4 (UInt32) - var max = Math.pow(2, this.nbBytes * 8) - 1; - if (safeValue > max) safeValue = max; // Clamp to max value allowed by the buffer size (prevents overflow crashes) - } - this.writeBufferCallback.call(s.buffer, safeValue, s.offset); + try { + this.writeBufferCallback.call(s.buffer, this.value, s.offset); + } + catch(e) { + if (e instanceof RangeError) { + throw new error.ProtocolError("NODE_RDP_CORE_TYPE_VALUE_OUT_OF_RANGE"); + } + throw e; + } s.offset += this._size_(); }; @@ -493,4 +493,4 @@ module.exports = { BinaryString : BinaryString, CallableValue : CallableValue, Factory : Factory -}; \ No newline at end of file +}; diff --git a/rdp/protocol/pdu/cliprdr.js b/rdp/protocol/pdu/cliprdr.js index 614a8bd806..82ab2790f0 100644 --- a/rdp/protocol/pdu/cliprdr.js +++ b/rdp/protocol/pdu/cliprdr.js @@ -36,10 +36,12 @@ class Client extends Cliprdr { this.transport.once('connect', (gccCore, userId, channelId) => { this.connect(gccCore, userId, channelId); - }).on('close', function () { - //this.emit('close'); - }).on('error', function (err) { - //this.emit('error', err); + }).on('close', () => { + log.debug('cliprdr : transport closed'); + this.emit('close'); + }).on('error', (err) => { + log.error('cliprdr : transport error ' + err); + this.emit('error', err); }); this.content = ''; 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/t125/mcs.js b/rdp/protocol/t125/mcs.js index 5248595795..5340cf1a9f 100644 --- a/rdp/protocol/t125/mcs.js +++ b/rdp/protocol/t125/mcs.js @@ -197,7 +197,7 @@ MCS.prototype.send = function(channelName, data) { * @param s {type.Stream} */ MCS.prototype.recv = function(s) { - opcode = new type.UInt8().read(s).value; + var opcode = new type.UInt8().read(s).value; if (readMCSPDUHeader(opcode, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM)) { log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM"); @@ -517,3 +517,4 @@ module.exports = { Client : Client, Server : Server }; + diff --git a/rdp/protocol/x224.js b/rdp/protocol/x224.js index c323b7dbab..66bb6b4d78 100644 --- a/rdp/protocol/x224.js +++ b/rdp/protocol/x224.js @@ -43,6 +43,19 @@ var NegotiationType = { TYPE_RDP_NEG_FAILURE : 0x03 }; +/** + * Failure codes present 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 */ @@ -228,7 +241,7 @@ Client.prototype.recvConnectionConfirm = function(s) { } if ([Protocols.PROTOCOL_HYBRID_EX].indexOf(this.selectedProtocol) !== -1) { - this.emit('error', 'NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED'); + this.emit('error', { err: 'NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED', code: 'NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED' }); return; //throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED'); } @@ -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/redirserver.js b/redirserver.js index dc9ee840ac..ad59d9f633 100644 --- a/redirserver.js +++ b/redirserver.js @@ -61,7 +61,7 @@ module.exports.CreateRedirServer = function (parent, db, args, func) { var i = rootcert.indexOf('-----BEGIN CERTIFICATE-----\r\n'); if (i >= 0) { rootcert = rootcert.substring(i + 29); } i = rootcert.indexOf('-----END CERTIFICATE-----'); - if (i >= 0) { rootcert = rootcert.substring(i, 0); } + if (i >= 0) { rootcert = rootcert.substring(0, i); } res.send(Buffer.from(rootcert, 'base64')); } else { res.sendStatus(404); @@ -164,3 +164,4 @@ module.exports.CreateRedirServer = function (parent, db, args, func) { return obj; }; +