1
0
Fork 0
mirror of https://github.com/Ylianst/MeshCentral.git synced 2025-03-09 15:40:18 +00:00

Compare commits

..

No commits in common. "master" and "MeshCentral_v1.0.44" have entirely different histories.

608 changed files with 41797 additions and 145186 deletions

1
.gitattributes vendored
View file

@ -1 +0,0 @@
*.sh text eol=lf

View file

@ -29,18 +29,12 @@ If applicable, add screenshots to help explain your problem.
- Network: [e.g. LAN/WAN, reverse proxy, cloudflare, ssl offload, etc...]
- Version: [e.g. 1.0.43]
- Node: [e.g. 18.4.0]
**Client Device (please complete the following information):**
- Device: [e.g. Laptop]
- OS: [e.g. Ubuntu]
- Network: [e.g. Local to Meshcentral, Remote over WAN]
- Browser: [e.g. Google Chrome]
- MeshCentralRouter Version: [if applicable]
**Remote Device (please complete the following information):**
- Device: [e.g. Laptop]
- OS: [e.g. Windows 10 21H2]
- Network: [e.g. Local to Meshcentral, Remote over WAN]
- OS: [e.g. Windows 10]
- Version: [e.g. 21H2]
- Current Core Version (if known): [**HINT**: Go to a device then `console` Tab then type `info`]
**Additional context**
@ -49,7 +43,7 @@ Add any other context about the problem here.
**Your config.json file**
```
{
"$schema": "https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json",
"$schema": "http://info.meshcentral.com/downloads/meshcentral-config-schema.json",
"__comment1__": "This is a simple configuration file, all values and sections that start with underscore (_) are ignored. Edit a section and remove the _ in front of the name. Refer to the user's guide for details.",
"__comment2__": "See node_modules/meshcentral/sample-config-advanced.json for a more advanced example.",
"settings": {

View file

@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Question
url: https://github.com/Ylianst/MeshCentral/discussions
about: Ask a question in discussions.
- name: Unofficial Discord Server
url: https://discord.gg/8wHC6ASWAc
about: Please ask here for support questions.
- name: Unoffical Telegram Channel
url: https://t.me/meshcentral
about: Please ask here for support questions.

View file

@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@ -26,7 +26,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
@ -48,4 +48,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v1

View file

@ -14,8 +14,8 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- run: pip install --upgrade pip

View file

@ -1,67 +0,0 @@
name: Docker
on:
push:
branches:
- master
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
check-token:
runs-on: ubuntu-latest
outputs:
token_defined: ${{ steps.token_check.outputs.token_defined }}
steps:
- name: Check token
id: token_check
env:
MY_TOKEN: ${{ secrets.MY_TOKEN }}
if: "${{ env.MY_TOKEN != '' }}"
run: echo "token_defined=true" >> "$GITHUB_OUTPUT"
build:
name: Release
runs-on: ubuntu-latest
needs: [check-token]
if: needs.check-token.outputs.token_defined == 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.MY_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
INCLUDE_MONGODBTOOLS=true
PREINSTALL_LIBS=true

View file

@ -1,25 +0,0 @@
name: Release
on:
push:
branches:
- master
paths:
- 'package.json'
jobs:
build:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Release
uses: justincy/github-action-npm-release@2.0.2
id: release
with:
token: ${{ secrets.MY_TOKEN }}
- name: Print release output
if: ${{ steps.release.outputs.released == 'true' }}
run: echo Release ID ${{ steps.release.outputs.release_id }}

4
.gitignore vendored
View file

@ -3,6 +3,7 @@
[Tt]yping/
[Ii]mages-spare/
[Dd]aemon/
[Bb]in/
[Aa]gent/
[Aa]gents/modules_meshcmd_min/
[Aa]gents/modules_meshcore_min/
@ -10,12 +11,12 @@
[Aa]gents/meshcore.min.js
[Pp]ublic/translations/
[Vv]iews/translations/
[Ee]mails/translations/
[Pp]ublic/*-min.htm
[Vv]iews/*-min.handlebars
meshcentral.db
meshcentral.db.json
mesherrors.txt
package-lock.json
bob.json
.greenlockrc
@ -41,6 +42,7 @@ bob.json
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

2
.npmrc
View file

@ -1 +1 @@
engine-strict = true
engine-strict=true

922
.vscode/settings.json vendored
View file

@ -1,922 +0,0 @@
{
"cSpell.words": [
"abcdf",
"accountchange",
"accountcreate",
"accountid",
"accountremove",
"acebase",
"acmd",
"acmepath",
"actiontype",
"adddevicegroup",
"adddeviceuser",
"adddomain",
"addmeshuser",
"addtousergroup",
"adduser",
"addusergroup",
"addusertodevice",
"addusertodevicegroup",
"addusertousergroup",
"adminaccount",
"adminname",
"agentaliasdns",
"agentaliasport",
"agentallowedip",
"agentapp",
"agentblockedip",
"agentconfig",
"agentconsole",
"agentcoredump",
"agentcoredumpusers",
"agentcustomization",
"agentdownload",
"agenterrorlogs",
"agentid",
"agentidletimeout",
"agentinfo",
"agentinvite",
"agentinvitecodes",
"agentkey",
"Agentless",
"agentnoproxy",
"agentport",
"agentportbind",
"agentporttls",
"agenttransfer",
"agenttype",
"agentupdateblocksize",
"agentupdatetest",
"agentwscompression",
"aliasport",
"allevents",
"allowaccountreset",
"allowframing",
"allowfullscreen",
"allowhighqualitydesktop",
"allowsavingdevicecredentials",
"allusers",
"alreadyinstalled",
"amtacmactivation",
"amtevents",
"amthost",
"amtmanager",
"amtoff",
"amton",
"amtonly",
"amtpass",
"amtreset",
"amtscanner",
"amtscanoptions",
"anewaccountcaptcha",
"apassword",
"apasswordhint",
"apikey",
"apos",
"appmetrics",
"apprelays",
"ashx",
"assistantconfig",
"assistantcustomization",
"assistantnoproxy",
"atag",
"authcookie",
"authenticode",
"authfail",
"authlog",
"authlogfile",
"Authn",
"authorizationurl",
"authstr",
"authstrategies",
"autofido",
"awsrds",
"backgroundcolor",
"backgroundonly",
"backupcode",
"backuppath",
"badargs",
"badtlscert",
"bancommonpasswords",
"batchupload",
"bitmask",
"Bounser",
"callbackurl",
"captchaargs",
"ccmp",
"Centralv",
"certbot",
"certfiles",
"certhash",
"certkeyhash",
"certpfx",
"certpfxpass",
"certurl",
"cfile",
"changedevice",
"changenode",
"changepassword",
"chatnotify",
"checkemail",
"checkmail",
"chnl",
"CIRA",
"ciraconn",
"ciralocalfqdn",
"ckey",
"clearpower",
"clientid",
"clientsecret",
"clipboardget",
"clipboardset",
"cmdoptions",
"cmds",
"cnonce",
"companyname",
"configfile",
"configfiles",
"configkey",
"connectionstring",
"Consts",
"cookieipcheck",
"cookiesamesite",
"coolofftime",
"coredump",
"coredumps",
"createaccount",
"createmesh",
"createusergroup",
"crowdsec",
"crypted",
"cscli",
"curloptionshttp",
"curloptionshttps",
"cuser",
"cuserid",
"customui",
"datafile",
"datapath",
"datas",
"datastr",
"dbconfig",
"dbdeleteconfigfiles",
"dbencryptkey",
"dbexport",
"dbexportmin",
"dbimport",
"dblistconfigfiles",
"dbmerge",
"dbpullconfigfiles",
"dbpulldatafiles",
"dbpushconfigfiles",
"dbshowconfigfile",
"debuglevel",
"defaultuserwebstate",
"deldump",
"deleteaccount",
"deletedefaultdomain",
"deletedomain",
"deletemesh",
"deleteuser",
"deleteusergroup",
"deluser",
"deluserpath",
"DESKLIMITEDINPUT",
"desktopmultiplex",
"desktopnotify",
"desktopprivacybar",
"desktopprompt",
"desktoprelays",
"desktopviewonly",
"devbox",
"devicefile",
"deviceid",
"deviceinfo",
"deviceinfocount",
"devicemessage",
"deviceopenurl",
"devicepower",
"devicepowerevents",
"devicesearchbarserverandclientname",
"deviceshare",
"devicesharing",
"devicetoast",
"devid",
"Digesthash",
"disablerequestedauthncontext",
"displayname",
"dlccore",
"dlcore",
"dldump",
"dnscount",
"dnssuffix",
"domaindefaults",
"domainid",
"domainname",
"domainurl",
"domainx",
"dont",
"dontlognull",
"downloadfile",
"dumpcores",
"dumpfile",
"editdevice",
"editdevicegroup",
"editgroup",
"editmesh",
"edituser",
"emailaddress",
"emailcheck",
"emaildomain",
"emailexists",
"emailok",
"emailvalidation",
"emailvalidationrequired",
"emailverified",
"entityid",
"entrypoints",
"errdesc",
"errlogpath",
"esversion",
"etype",
"eventlogger",
"exactport",
"exactports",
"exphbs",
"extractall",
"extrakey",
"extralinks",
"extrascriptsrc",
"factorauth",
"factorwarning",
"fadev",
"fahold",
"fasent",
"fastcert",
"fchallenge",
"fileaccess",
"filedata",
"filefullpath",
"filenotify",
"fileprompt",
"filesize",
"filespath",
"filestats",
"fileurl",
"filteredusers",
"filterid",
"firebaserelay",
"firstname",
"forceduserwebstate",
"foregroundcolor",
"forwardclient",
"forwardfor",
"forwardwrite",
"forwardwsocket",
"fpath",
"Freemonitoring",
"frontends",
"ftarget",
"fullpath",
"fullrights",
"fullscreen",
"gatewaymac",
"generateinvitelink",
"geourl",
"getnetworkinfo",
"getsysinfo",
"getwspass",
"googleusercontent",
"gotodevicename",
"gotonode",
"groupid",
"guestdevicesharing",
"guestname",
"GUESTSHARING",
"hashhex",
"Hashi",
"hashpass",
"hashpasssplit",
"hashpassword",
"Hashs",
"healthcheck",
"Hilaire",
"hkey",
"httpheaders",
"httplog",
"httpport",
"hwchallenge",
"hwotp",
"hwstate",
"hwtoken",
"Ider",
"idexists",
"idhex",
"idpurl",
"idsplit",
"iframe",
"ignoreagenthashcheck",
"iishash",
"imagebase",
"imagefile",
"indexagenterrorlog",
"indexmcrec",
"installflags",
"installsize",
"installtext",
"intelamt",
"interactiveonly",
"interuser",
"invitecodes",
"ipaddr",
"ipblockeduserredirect",
"ipcheck",
"ipex",
"ipkvm",
"iplayer",
"ipranges",
"isaml",
"Jitsi",
"jumpcloud",
"keyfile",
"keygrip",
"keyid",
"lanonly",
"LAPI",
"lastaddr",
"lastconnect",
"lastname",
"ldapauth",
"ldapobj",
"ldapoptions",
"ldapsaveusertofile",
"ldapsyncwithusergroups",
"ldapuserbinarykey",
"ldapuseremail",
"ldapusergroups",
"ldapuserimage",
"ldapuserkey",
"ldapusername",
"ldapuserphonenumber",
"ldapuserrealname",
"ldapuserrequiredgroupmembership",
"ldapusers",
"leok",
"letsencrypt",
"lightgray",
"limiteddesktop",
"limitedevents",
"LIMITEVENTS",
"Linaro",
"linuxpath",
"listdevicegroups",
"listdevices",
"listdomains",
"listevents",
"listusergroups",
"listuserids",
"listusers",
"listusersessions",
"listusersofdevicegroup",
"loadconfigfromdb",
"localdiscovery",
"localfile",
"localpath",
"localrelay",
"localsessionrecording",
"localurl",
"lockagentdownload",
"locksettings",
"logfile",
"logincodeb",
"logindomain",
"loginfooter",
"loginkey",
"loginkeyfile",
"loginlogo",
"loginmode",
"loginpass",
"loginpicture",
"loginscreen",
"logintoken",
"logintokengen",
"logintokenkey",
"logintokens",
"loginuser",
"logoback",
"logoutcontrols",
"logouturl",
"macrouter",
"magenturl",
"mailserver",
"mailtokengen",
"maintenancemode",
"mainwelcome",
"MANAGECOMPUTERS",
"managedevices",
"manageusers",
"markcoredump",
"maxfidokeys",
"maxlen",
"maxuseraccounts",
"mcpath",
"mcrdesktop",
"mcrec",
"mcrfiles",
"mcrouter",
"Mebx",
"meshaction",
"meshadmin",
"meshagent",
"meshagents",
"meshauth",
"meshcentral",
"meshcentralhost",
"meshchange",
"meshcmd",
"meshcommander",
"meshcookie",
"meshcore",
"meshctrl",
"meshdesktopmultiplex",
"meshdevicefile",
"mesherrorlogpath",
"mesherrors",
"meshfilename",
"meshid",
"meshidhex",
"meshidname",
"meshinstall",
"meshmail",
"meshmessenger",
"meshmessengerid",
"meshmessengerpicture",
"meshmessengertitle",
"meshname",
"meshosxagent",
"meshquota",
"meshrelay",
"MESHRIGHT",
"meshrights",
"meshscanner",
"meshserver",
"meshsettings",
"meshsettingslines",
"meshtype",
"meshuser",
"Messagebox",
"messageid",
"Messenging",
"minfo",
"minifyall",
"minifycore",
"mongodbcol",
"mongodump",
"mongorestore",
"moutput",
"movetodevicegroup",
"mpkg",
"mpsaliasport",
"mpscert",
"mpsdebug",
"mpspass",
"mpsport",
"mpsserver",
"mpsservers",
"MPSSSL",
"mpstlsoffload",
"mqttbroker",
"MSCHA",
"msgid",
"mstsc",
"mstscrelay",
"mtype",
"multiplexor",
"multiresponse",
"multivalued",
"myaccountname",
"mycompany",
"mydomain",
"mypassword",
"myserver",
"myservername",
"nameexists",
"nedbtodb",
"netif",
"newaccountemaildomains",
"newaccountname",
"newaccountrealms",
"newaccounts",
"newaccountscaptcha",
"newaccountspass",
"newaccountsrights",
"newaccountsusergroups",
"newgroupname",
"newobj",
"newpass",
"newpassword",
"NGNIX",
"nightmode",
"noact",
"noagentupdate",
"noamt",
"noauth",
"noav",
"nodeconnect",
"nodecount",
"nodeid",
"nodeids",
"nodeidsplit",
"nodeinfo",
"nodekey",
"nodepath",
"NODESKTOP",
"nodewindows",
"nofiles",
"nofirewall",
"nolog",
"nologout",
"NOMESHCMD",
"nominify",
"nonalpha",
"NONEWDEVICES",
"nonewgroups",
"noproxy",
"noredirect",
"nosniff",
"noterminal",
"notools",
"nouser",
"nousers",
"novnc",
"npmjs",
"npmpath",
"npmproxy",
"npmtag",
"objid",
"ODELAY",
"offloader",
"offloaders",
"oidc",
"oldpassword",
"oldpasswordban",
"oldpasswords",
"oneclickrecovery",
"onlyselecteddevicegroups",
"onlyselectedusers",
"openidconnect",
"openstreetmap",
"openurl",
"orphanagentuser",
"osdesc",
"osinfo",
"otpdev",
"otpekey",
"otpemail",
"otphkeys",
"otpkeys",
"otplib",
"otppush",
"otpsecret",
"otpsms",
"parentpath",
"passchange",
"passhint",
"passlogin",
"passrequirementstr",
"passtype",
"passwordrequirements",
"passwordrequirementsstr",
"pastlogin",
"pathx",
"peinfo",
"phonenumber",
"PKCK",
"plivo",
"pluginadmin",
"plusplus",
"portbind",
"postflight",
"poweraction",
"powerevents",
"Preconfigured",
"Proto",
"publicid",
"pushlogin",
"pushrelay",
"pushrelayserver",
"qport",
"randompass",
"Raritan",
"rauth",
"rawdata",
"rcookie",
"rdpport",
"realname",
"recordencryptionrecode",
"recordpath",
"redir",
"rediraliasport",
"redirections",
"redirport",
"redirserver",
"refreshtoken",
"relayaliasport",
"relaydns",
"relayid",
"relayport",
"relayserver",
"relaysession",
"remembertoken",
"remoteaddr",
"remoteaddrport",
"REMOTECOMMAND",
"remotecontrol",
"remotefile",
"remotepath",
"REMOTEVIEWONLY",
"removeallusersfromusergroup",
"removedevicegroup",
"removedomain",
"removefromdomain",
"removefromusergroup",
"removemeshuser",
"removesubdomain",
"removetestagents",
"removeuser",
"removeuserfromdevice",
"removeuserfromdevicegroup",
"removeuserfromusergroup",
"removeusergroup",
"resetaccount",
"RESETOFF",
"resetpass",
"responseid",
"restoreserver",
"rightsstr",
"rname",
"rnamel",
"rootcert",
"rootredirect",
"rpassword",
"rpasswordhint",
"rport",
"rtpass",
"rtuser",
"runas",
"runasuser",
"runasuseronly",
"runcommand",
"runcommands",
"runmode",
"runonservererror",
"runonserverupdated",
"ruserid",
"sameorigin",
"selfupdate",
"selfurl",
"senderid",
"sendgrid",
"sendinviteemail",
"serialtunnel",
"SERVERBACKUP",
"serverfeatures",
"serverfiles",
"serverhttps",
"serverid",
"serveridhex",
"serverinfo",
"serverkey",
"servername",
"servernoproxy",
"serverpath",
"serverpic",
"serverport",
"SERVERRESTORE",
"servertlshash",
"serverupdate",
"servicename",
"servicepath",
"sessioncode",
"sessionkey",
"sessionrecording",
"sessionsamesite",
"sessiontime",
"setbad",
"SETNOTES",
"settodomain",
"sftpconnect",
"shareid",
"showagents",
"showall",
"showallmeshes",
"showevents",
"showiplocations",
"showitem",
"showmeshes",
"shownodes",
"showpasswordlogin",
"showpower",
"showsmbios",
"showusergroups",
"showusers",
"showversion",
"siteadmin",
"SITERIGHT",
"sitestyle",
"smsserver",
"specificupdate",
"splitip",
"splitpath",
"spliturl",
"srights",
"sshconnect",
"sshfilesrelay",
"sshport",
"sshrelay",
"sshterminalrelay",
"ssid",
"sspi",
"startack",
"statsevents",
"stricttransportsecurity",
"Strs",
"subdir",
"swarmallowedip",
"swarmport",
"swarmserver",
"sysinfo",
"syslogauth",
"syslogjson",
"syslogtcp",
"tcpport",
"telnyx",
"temail",
"tenantid",
"terminalnotify",
"terminalprompt",
"termsize",
"timedoc",
"titleid",
"titlepicture",
"tkip",
"tlscertcheck",
"tlshash",
"tlsock",
"tlsoffload",
"tlsoptions",
"tlsrootcert",
"tlsstrict",
"tmpdl",
"tokenemail",
"tokenlogin",
"tokenpassword",
"tokenpush",
"tokenrequired",
"tokensms",
"tokenurl",
"tokenuserid",
"tokenusername",
"totalsize",
"TOTP",
"tpass",
"tpassword",
"tpush",
"traefik",
"translateall",
"translationpath",
"trustedcert",
"trustedproxy",
"tsms",
"TTLS",
"tunnelws",
"tunnelwsstate",
"tuser",
"tuserid",
"tusername",
"twofactor",
"twofactorcookiedurationdays",
"twofactortimeout",
"tzoffset",
"uaparser",
"ucookie",
"ugroup",
"ugroups",
"ugrp",
"ugrpid",
"uicustomevent",
"unadmin",
"unknownuserrootredirect",
"unsealkey",
"updatefiles",
"uploadack",
"uploaderror",
"uploadfile",
"uploadfilebatch",
"uploadmeshcorefile",
"uploadstart",
"urlpath",
"urlswitching",
"useid",
"userallowedip",
"userblockedip",
"userbroadcast",
"userconsentflags",
"usercount",
"userex",
"userfiles",
"userfirst",
"usergroupchange",
"usergroups",
"userid",
"userids",
"userimage",
"userinfourl",
"usernameisemail",
"userquota",
"userrequiredhttpheader",
"Usersessionidletimeout",
"usersid",
"usersplit",
"vaultdeleteconfigfiles",
"vaultpullconfigfiles",
"vaultpushconfigfiles",
"verifyemail",
"Viewmode",
"viewonly",
"WAKEDEVICE",
"wakedevices",
"Walkthru",
"wanonly",
"Webauthn",
"webcerthash",
"webdefault",
"webemailspath",
"webider",
"webpublicpath",
"webpush",
"webrelay",
"webrelaydata",
"webrelayserver",
"webrequest",
"webrtc",
"webrtconfig",
"webserver",
"websockets",
"WEBSSL",
"webstate",
"webviewspath",
"WELCOMEMSG",
"welcomepicture",
"welcomepicturefullscreen",
"welcometext",
"wgetoptionshttp",
"wgetoptionshttps",
"wildleek",
"winassistant",
"winpath",
"winrouter",
"winservice",
"wsagents",
"wscompression",
"wsrelays",
"wssessioncount",
"wssessions",
"xarg",
"xbytes",
"xcmd",
"xdomain",
"xdomains",
"xenv",
"xevents",
"xfile",
"xfilelen",
"xfilepath",
"xflags",
"xforwardedhost",
"xinstall",
"xjslint",
"xmeshes",
"xpad",
"xpassword",
"xrelay",
"xrestart",
"xstate",
"xtls",
"xtransport",
"xuninstall",
"xuserid",
"xusername",
"xxdata",
"xxprocess",
"xxurl",
"xxuser",
"xxxprocess",
"Ylian",
"yubikey",
"yubikeyotp",
"zdata",
"zipfile"
]
}

View file

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017-2025 Intel Corporation
Copyright 2017-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.

View file

@ -35,7 +35,6 @@
<Compile Include="agents\modules_meshcmd\amt-wsman.js" />
<Compile Include="agents\modules_meshcmd\amt-xml.js" />
<Compile Include="agents\modules_meshcmd\amt.js" />
<Compile Include="agents\modules_meshcmd\linux-dhcp.js" />
<Compile Include="agents\modules_meshcmd\smbios.js" />
<Compile Include="agents\modules_meshcmd\sysinfo.js" />
<Compile Include="agents\modules_meshcmd\win-securitycenter.js" />
@ -101,7 +100,6 @@
<Compile Include="amt\amt-xml.js" />
<Compile Include="amt\amt.js" />
<Compile Include="authenticode.js" />
<Compile Include="crowdsec.js" />
<Compile Include="exeHandler.js" />
<Compile Include="amtprovisioningserver.js" />
<Compile Include="firebase.js" />
@ -113,7 +111,6 @@
<Compile Include="meshdesktopmultiplex.js" />
<Compile Include="meshipkvm.js" />
<Compile Include="meshmail.js" />
<Compile Include="meshmessaging.js" />
<Compile Include="meshrelay.js" />
<Compile Include="meshsms.js" />
<Compile Include="meshscanner.js" />
@ -239,8 +236,8 @@
<Compile Include="public\scripts\common-0.0.1.js" />
<Compile Include="public\scripts\meshcentral.js" />
<Compile Include="redirserver.js" />
<Compile Include="taskmanager.js" />
<Compile Include="translate\translate.js" />
<Compile Include="ua-parser.js" />
<Compile Include="webauthn.js" />
<Compile Include="webrelayserver.js" />
<Compile Include="webserver.js" />
@ -440,6 +437,7 @@
<Content Include="LICENSE" />
<Content Include="meshcentral-config-schema.json" />
<Content Include="package.json" />
<Content Include="plugin_development.md" />
<Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.application" />
<Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.exe.config.deploy" />
<Content Include="public\clickonce\minirouter\Application Files\MeshMiniRouter_1_0_0_70\MeshMiniRouter.exe.deploy" />
@ -594,14 +592,12 @@
<Content Include="readme.md" />
<Content Include="sample-config-advanced.json" />
<Content Include="sample-config.json" />
<Content Include="SECURITY.md" />
<Content Include="SourceFileList.txt" />
<Content Include="translate\readme.txt" />
<Content Include="translate\translate.json" />
<Content Include="views\agentinvite.handlebars" />
<Content Include="views\default-mobile.handlebars" />
<Content Include="views\default.handlebars" />
<Content Include="views\default3.handlebars" />
<Content Include="views\download.handlebars" />
<Content Include="views\download2.handlebars" />
<Content Include="views\error404-mobile.handlebars" />
@ -681,8 +677,6 @@
<Folder Include="typings\globals\ajv\" />
<Folder Include="typings\globals\async\" />
<Folder Include="typings\globals\axios\" />
<Folder Include="typings\globals\big-integer\" />
<Folder Include="typings\globals\busboy\" />
<Folder Include="typings\globals\connect-redis\" />
<Folder Include="typings\globals\cookie-session\" />
<Folder Include="typings\globals\core-js\" />
@ -696,7 +690,6 @@
<Folder Include="typings\globals\he\" />
<Folder Include="typings\globals\hooker\" />
<Folder Include="typings\globals\http-errors\" />
<Folder Include="typings\globals\ip\" />
<Folder Include="typings\globals\is-plain-object\" />
<Folder Include="typings\globals\jsbn\" />
<Folder Include="typings\globals\klaw\" />
@ -713,12 +706,9 @@
<Folder Include="typings\globals\once\" />
<Folder Include="typings\globals\passport\" />
<Folder Include="typings\globals\pg-pool\" />
<Folder Include="typings\globals\rx-lite\" />
<Folder Include="typings\globals\split2\" />
<Folder Include="typings\globals\sprintf-js\" />
<Folder Include="typings\globals\sqlite3\" />
<Folder Include="typings\globals\type-check\" />
<Folder Include="typings\globals\ua-parser-js\" />
<Folder Include="typings\globals\underscore\" />
<Folder Include="typings\globals\uuid\" />
<Folder Include="typings\globals\window-size\" />
@ -728,8 +718,6 @@
<TypeScriptCompile Include="typings\globals\ajv\index.d.ts" />
<TypeScriptCompile Include="typings\globals\async\index.d.ts" />
<TypeScriptCompile Include="typings\globals\axios\index.d.ts" />
<TypeScriptCompile Include="typings\globals\big-integer\index.d.ts" />
<TypeScriptCompile Include="typings\globals\busboy\index.d.ts" />
<TypeScriptCompile Include="typings\globals\connect-redis\index.d.ts" />
<TypeScriptCompile Include="typings\globals\cookie-session\index.d.ts" />
<TypeScriptCompile Include="typings\globals\core-js\index.d.ts" />
@ -743,7 +731,6 @@
<TypeScriptCompile Include="typings\globals\he\index.d.ts" />
<TypeScriptCompile Include="typings\globals\hooker\index.d.ts" />
<TypeScriptCompile Include="typings\globals\http-errors\index.d.ts" />
<TypeScriptCompile Include="typings\globals\ip\index.d.ts" />
<TypeScriptCompile Include="typings\globals\is-plain-object\index.d.ts" />
<TypeScriptCompile Include="typings\globals\jsbn\index.d.ts" />
<TypeScriptCompile Include="typings\globals\klaw\index.d.ts" />
@ -760,12 +747,9 @@
<TypeScriptCompile Include="typings\globals\once\index.d.ts" />
<TypeScriptCompile Include="typings\globals\passport\index.d.ts" />
<TypeScriptCompile Include="typings\globals\pg-pool\index.d.ts" />
<TypeScriptCompile Include="typings\globals\rx-lite\index.d.ts" />
<TypeScriptCompile Include="typings\globals\split2\index.d.ts" />
<TypeScriptCompile Include="typings\globals\sprintf-js\index.d.ts" />
<TypeScriptCompile Include="typings\globals\sqlite3\index.d.ts" />
<TypeScriptCompile Include="typings\globals\type-check\index.d.ts" />
<TypeScriptCompile Include="typings\globals\ua-parser-js\index.d.ts" />
<TypeScriptCompile Include="typings\globals\underscore\index.d.ts" />
<TypeScriptCompile Include="typings\globals\uuid\index.d.ts" />
<TypeScriptCompile Include="typings\globals\window-size\index.d.ts" />

View file

@ -1,49 +0,0 @@
# Security Policy
## Supported Versions
Any version of MeshCentral 1.x.x is supported.
| Version | Supported |
| ------- | ------------------ |
| 1.x.x | :white_check_mark: |
| < 1.0 | :x: |
## Reporting a Vulnerability
Please report any concerns or security issue to Ylian Saint-Hilaire (ylianst@gmail.com). If needed, use my PGP key below.
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.56
mQMuBF2gC4sRCAClFNvMCCVW3ego3UHBQ6LhSenJfaZYhvn8gaGuemSQxqTI6bla
BTAv3aMtQnvqlSuadMMegb+FO6hnaQMlGvpVA1qpkSzgrPS5HrBD3H33J2Nj3i93
ZpDPpxdI0ehCj6IJPnl0GxGbpKIN8YpJUFl44wv1lMRFI1lgyb+dCoO60irYdNQB
PV85BI+DwPfOBFHunwR78nqMvpvsk9HaeHjEP7oXr952/7EazUowZsMlEfkYnw5S
+tLfpCoY3QWkektpJP40nMJSKQdV2NEuED99doA0X+7P1vsvFFFyMH69dnU2uSay
XCHpkAbntBy0BGmtF1RnTcOMv2V/LPXnlMdvAQCbmLQzNra3r163tcdRY0jSs+pZ
1L3w5tHNj2dzhfpa7wf/SIuds6QTr2LCN6miLoSVCRMMpT7d771b16GwQqWEXzN2
+h7dYqrssHPOa8FSUrPerz0+0eFcbMSm5/L/4KXWXoQthURv8aMP9E0iVoUYaaKB
7U+5vFEZbpoOZyZmTAjXQMSNZCft0azA82Q+G85euyicWtMv48yNVzUhkdh+M2ud
ohkXX2Aor1TqpBJoIeWke7j9D+Bo+lu61zPRx5ed9teUeLJCwqNEjlE+6gre5kxF
PoreAtn59QYcBIpzQEWVMbNFlDAR4jMyqIoKCGfBPiRw2V+kunbzqiGQEglIFfOt
6sTN/+CJh0ei976VDmE0Z1kMN+CNLgIjIw8fl02V9QgAnHcpqtVUxR4dbGOhVDq5
lWv+K75QQlWyXC2k+KboXcaCvH0WZEBACYzO0CfrZ5hP9BSkbj5usSUVGGHwEFAJ
t+/04KVY71fW281Ej5kGNaIKxeKsx6+hMo+UXb5ZM+6fANNNxs1cK95sTH6PjkyB
tsKxLoa3CV2v9mSE5JiKKt74R9nXVo7PXf6DizwAU2l30Lb6y6y0OdXdCCPAG8Ij
FrMgPu5MtjgsO5DnkZfUqDPWHhOgEPyOh3Ho+pvDhNYh5cm2eLQ8g5orzs2FHwbZ
DpAHwCdqrlcpBlKJ4W/MZdf1fg2PjqaTWm7ZFiGr91P0F6kltTLWbVKTjLdS0T+D
L7QnWWxpYW4gU2FpbnQtSGlsYWlyZSA8eWxpYW5zdEBnbWFpbC5jb20+iF4EExEI
AAYFAl2gC4sACgkQg7j/r4DH+kD/3gD+MRedlM53VzOtNOpS6mqDAxj1aWP90HN0
AqO6zuCTyGgBAJlunLFKH8IUetmQOhiohB8HVhdm/q4lKRDV7sHdplDyuMwEXaAL
ixACAJSU/sCV87he4oZUKzg2/IGl3QoDSbTCOd04dE1IjPjjHbi8t9M7Qau55aM8
ypFEsc7zMslL8Fc78EejrKmM3zsB/RU9XWFyrbQwRbaK6OHeEHC2E3AFaG0p09c6
d0kZloHuWyEsm5a/3PpbIM1eP9IESJXWCc+bQQt6DxLKHLmkKMwB/icWMg8uMJlx
aady8TEq7LH5oFVKsglnwuN1nIkecrf77TVkEqTjIxS6TiOup6zOnioFNKLYBAH0
WUnJEYFvx4OIXgQYEQgABgUCXaALiwAKCRCDuP+vgMf6QGFTAQCUj2gGwsFlN0eR
Wowv4eLcc3FwQ+lBElUctKg8vNFb0gD/ZWVWsWwKerNgNnf7RGD9mt8G2CKvdgGG
oZ2hPP2gU9w=
=roW4
-----END PGP PUBLIC KEY BLOCK-----
```

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -34,10 +34,10 @@
"agent": "Agent",
"group": "Skupina zařízení",
"url": "URL serveru",
"meshName": "Skupinové jméno",
"meshName": "Název oka",
"meshId": "Identifikátor skupiny",
"serverId": "Identifikátor serveru",
"setup": "Nastavit",
"setup": "Založit",
"update": "Aktualizace",
"install": "Instalace",
"uninstall": "Odinstalace",
@ -52,7 +52,7 @@
"zenity": "Zkuste nainstalovat / aktualizovat Zenity a spustit znovu",
"status": [
"NENÍ INSTALOVÁN",
"SPUŠTĚNO",
"BĚH",
"NEFUNGUJE"
],
"statusDescription": "Aktuální stav agenta",
@ -65,11 +65,11 @@
"agent": "Agent",
"group": "Gerätegruppe",
"url": "Server-URL",
"meshName": "Gruppenname",
"meshName": "MESH Name",
"meshId": "Gruppen-ID",
"serverId": "Server-ID",
"setup": "Konfiguration",
"update": "Aktualisierung",
"update": "Updates",
"install": "Installieren",
"uninstall": "Deinstallation",
"connect": "Verbinden",
@ -86,7 +86,7 @@
"GESTARTET",
"NICHT GESTARTET"
],
"statusDescription": "Aktueller Agentstatus",
"statusDescription": "Aktueller Agentenstatus",
"agentVersion": "Neue Version",
"elevation": "Zum Installieren/Deinstallieren dieser Software sind erhöhte Berechtigungen erforderlich.",
"graphicalerror": "Die grafische Version dieses Installationsprogramms kann auf diesem System nicht ausgeführt werden",
@ -98,7 +98,7 @@
"agent": "Agente",
"group": "Grupo de Dispositivos",
"url": "URL del servidor",
"meshName": "Nombre del grupo",
"meshName": "Nombre de malla",
"meshId": "Identificador del Grupo",
"serverId": "Identificador de Servidor",
"setup": "Preparar",
@ -129,7 +129,7 @@
"agent": "Agentti",
"group": "Laiteryhmä",
"url": "Palvelimen URL-osoite",
"meshName": "Ryhmän nimi",
"meshName": "Verkon nimi",
"meshId": "Ryhmän tunniste",
"serverId": "Palvelimen tunniste",
"setup": "Perustaa",
@ -191,7 +191,7 @@
"agent": "एजेंट",
"group": "डिवाइस समूह",
"url": "सर्वर URL",
"meshName": "समूह नाम",
"meshName": "मेष नाम",
"meshId": "समूह पहचानकर्ता",
"serverId": "सर्वर पहचानकर्ता",
"setup": "सेट अप",
@ -222,7 +222,7 @@
"agent": "Agente",
"group": "Gruppo di dispositivi",
"url": "URL del server",
"meshName": "Nome del gruppo",
"meshName": "Nome mesh",
"meshId": "Identificatore di gruppo",
"serverId": "Identificatore del server",
"setup": "Impostare",
@ -253,7 +253,7 @@
"agent": "エージェント",
"group": "デバイスグループ",
"url": "サーバーのURL",
"meshName": "그룹 이름",
"meshName": "メッシュ名",
"meshId": "グループ識別子",
"serverId": "サーバー識別子",
"setup": "セットアップ",
@ -284,7 +284,7 @@
"agent": "에이전트",
"group": "장치 그룹",
"url": "서버의 위치",
"meshName": "그룹 이름",
"meshName": "메시의 이름",
"meshId": "그룹 식별자",
"serverId": "서버의 식별자",
"setup": "설정하다",
@ -305,11 +305,7 @@
"운영",
"중지됨"
],
"statusDescription": "에이전트 상태",
"agentVersion": "새로운 버전",
"elevation": "이 소프트웨어를 설치/제거하려면 높은 권한이 필요합니다.",
"graphicalerror": "이 설치 프로그램의 그래픽 버전은 이 시스템에서 실행할 수 없습니다.",
"description": "이 원격 관리 소프트웨어를 설치하거나 제거하려면 아래 버튼을 클릭하십시오. 이 소프트웨어를 설치하면 백그라운드에서 실행되어 원격 관리자가 이 컴퓨터를 관리하고 제어할 수 있습니다."
"statusDescription": "에이전트 상태"
},
"nl": {
"agent": "Agent",
@ -346,7 +342,7 @@
"agent": "Agente",
"group": "Grupo de dispositivos",
"url": "URL do servidor",
"meshName": "Nome do grupo",
"meshName": "Nome da malha",
"meshId": "Identificador de Grupo",
"serverId": "Identificador de Servidor",
"setup": "Configuração",
@ -367,11 +363,7 @@
"CORRIDA",
"NÃO CORRENDO"
],
"statusDescription": "Status atual do agente",
"agentVersion": "Nova versão",
"elevation": "Permissões elevadas são necessárias para instalar/desinstalar este software.",
"graphicalerror": "A versão gráfica deste instalador não pode ser executada neste sistema",
"description": "Clique nos botões abaixo para instalar ou desinstalar este software de gerenciamento remoto. Quando instalado, este software é executado em segundo plano, permitindo que este computador seja gerenciado e controlado por um administrador remoto."
"statusDescription": "Status atual do agente"
},
"ru": {
"agent": "Агент",
@ -408,7 +400,7 @@
"agent": "Agent",
"group": "Enhetsgrupp",
"url": "Serverns URL",
"meshName": "Grupp namn",
"meshName": "Masknamn",
"meshId": "Gruppidentifierare",
"serverId": "Serveridentifierare",
"setup": "Uppstart",
@ -429,17 +421,13 @@
"LÖPNING",
"SPRINGER INTE"
],
"statusDescription": "Aktuell agentstatus",
"agentVersion": "Ny version",
"elevation": "Förhöjda behörigheter krävs för att installera/avinstallera denna programvara.",
"graphicalerror": "Den grafiska versionen av detta installationsprogram kan inte köras på det här systemet",
"description": "Klicka på knapparna nedan för att installera eller avinstallera denna fjärrhanteringsprogramvara. När den är installerad körs den här programvaran i bakgrunden så att den här datorn kan hanteras och kontrolleras av en fjärradministratör."
"statusDescription": "Aktuell agentstatus"
},
"tr": {
"agent": "Agent",
"group": "Cihaz Grubu",
"url": "Sunucu URL'si",
"meshName": "Grup ismi",
"meshName": "Mesh Adı",
"meshId": "Grup Tanımlayıcı",
"serverId": "Sunucu Tanımlayıcı",
"setup": "Kurmak",
@ -470,11 +458,11 @@
"agent": "代理",
"group": "设备组",
"url": "服务器网址",
"meshName": "团队名字",
"meshName": "网格名称",
"meshId": "组标识符",
"serverId": "服务器标识符",
"setup": "设定",
"update": "更新",
"update": "更新资料",
"install": "安装",
"uninstall": "卸载",
"connect": "连接",
@ -491,17 +479,13 @@
"正在运行",
"不在运行"
],
"statusDescription": "当前代理状态",
"agentVersion": "新版本",
"elevation": "安装/卸载此软件需要提升权限。",
"graphicalerror": "此安装程序的图形版本无法在此系统上运行",
"description": "单击下面的按钮以安装或卸载此远程管理软件。安装后,该软件在后台运行,允许远程管理员管理和控制该计算机。"
"statusDescription": "当前代理状态"
},
"zh-cht": {
"agent": "代理",
"group": "裝置群",
"url": "服務器網址",
"meshName": "團隊名字",
"meshName": "網格名稱",
"meshId": "群標識符",
"serverId": "服務器標識符",
"setup": "設定",
@ -522,17 +506,13 @@
"正在運行",
"不在運行"
],
"statusDescription": "當前代理狀態",
"agentVersion": "新版本",
"elevation": "安裝/卸載此軟件需要提升權限。",
"graphicalerror": "此安裝程序的圖形版本無法在此系統上運行",
"description": "單擊下面的按鈕以安裝或卸載此遠程管理軟件。安裝後,該軟件在後台運行,允許遠程管理員管理和控制該計算機。"
"statusDescription": "當前代理狀態"
},
"da": {
"agent": "Agent",
"group": "Enhedsgruppe",
"url": "Server URL",
"meshName": "Gruppe navn",
"meshName": "Mesh Navn",
"meshId": "Gruppe-id",
"serverId": "Serveridentifikator",
"setup": "Opsætning",
@ -620,129 +600,5 @@
"elevation": "Permissões Elevadas são necessárias para instalar/desinstalar este software",
"graphicalerror": "A versão gráfica do instalador não pode ser executada neste sistema",
"description": "Clique nos botões abaixo para instalar ou desinstalar este software de gerenciamento remoto. Quando instalado, este software é executado em segundo plano permitindo que este computador seja gerenciado e controlado por um administrador remoto"
},
"bs": {
"agent": "Agent",
"agentVersion": "Nova verzija",
"group": "Grupa uređaja",
"url": "URL servera",
"meshName": "Ime grupe",
"meshId": "Grupni identifikator",
"serverId": "Identifikator servera",
"setup": "Postaviti",
"update": "Ažuriraj",
"install": "Instaliraj",
"uninstall": "Deinstalirati",
"connect": "Povežite se",
"disconnect": "Prekini vezu",
"cancel": "Otkaži",
"close": "Zatvori",
"pressok": "Pritisnite OK da prekinete vezu",
"elevation": "Za instaliranje/deinstaliranje ovog softvera potrebne su povišene dozvole.",
"sudo": "Molimo pokušajte ponovo sa sudo.",
"ctrlc": "Pritisnite Ctrl-C za izlaz.",
"commands": "Možete pokrenuti tekstualnu verziju iz komandne linije sa sljedećim naredbama",
"graphicalerror": "Grafička verzija ovog instalatera ne može da radi na ovom sistemu",
"zenity": "Pokušajte instalirati/ažurirati Zenity i pokrenite ponovo",
"status": [
"NIJE INSTALIRANO",
"RUNNING",
"NOT RUNNING"
],
"statusDescription": "Trenutni status agenta",
"description": "Kliknite na dugmad ispod da instalirate ili deinstalirate ovaj softver za daljinsko upravljanje. Kada je instaliran, ovaj softver radi u pozadini, što omogućava da ovim računarom upravlja i kontroliše udaljeni administrator."
},
"hu": {
"agent": "Agent",
"agentVersion": "Új verzió",
"group": "Eszköz csoport",
"url": "Kiszolgáló URL",
"meshName": "Csoport név",
"meshId": "Csoport azonosító",
"serverId": "Kiszolgáló azonosító",
"setup": "Beállítás",
"update": "Frissítés",
"install": "Telepítés",
"uninstall": "Eltávolítás",
"connect": "Kapcsolódás",
"disconnect": "Lekapcsolódás",
"cancel": "Mégse",
"close": "Bezár",
"pressok": "Press OK to disconnect",
"elevation": "A szoftver telepítéséhez/eltávolításához megnövelt jogosultságok szükségesek.",
"sudo": "Kérjük, próbálja meg újra a sudo használatával.",
"ctrlc": "A kilépéshez nyomja meg a Ctrl-C billentyűt.",
"commands": "A szöveges változatot a parancssorból futtathatja a következő parancs(okk)al",
"graphicalerror": "A telepítő grafikus verziója nem futtatható ezen a rendszeren.",
"zenity": "Próbálja meg telepíteni/frissíteni a Zenity-t, és indítsa újra",
"status": [
"NINCS TELEPÍTVE",
"FUT",
"NEM FUT"
],
"statusDescription": "Jelenlegi agent állapota",
"description": "Kattintson a Telepítés vagy Eltávolítás gombokra a Távfelügyeleti alkalmazás telepítéséhez vagy eltávolításához. Telepítés után ez az alkalmazás a háttérben fut, lehetővé téve, hogy a számítógépet egy távoli rendszergazda kezelje."
},
"ca": {
"agent": "Agent",
"agentVersion": "Nova versió",
"group": "Grup de dispositius",
"url": "URL del servidor",
"meshName": "Nom del grup",
"meshId": "Identificador de grup",
"serverId": "Identificador del servidor",
"setup": "Configuració",
"update": "Actualització",
"install": "Instal·lar",
"uninstall": "Desinstal·la",
"connect": "Connecta't",
"disconnect": "Desconnecta",
"cancel": "Cancel · lar",
"close": "Tanca",
"pressok": "Premeu D'acord per desconnectar",
"elevation": "Es necessiten permisos elevats per instal·lar/desinstal·lar aquest programari.",
"sudo": "Si us plau, torna-ho a provar amb sudo.",
"ctrlc": "Premeu Ctrl-C per sortir.",
"commands": "Podeu executar la versió de text des de la línia d'ordres amb les següents ordres",
"graphicalerror": "La versió gràfica d'aquest instal·lador no pot executar-se en aquest sistema",
"zenity": "Proveu d'instal·lar/actualitzar Zenity i torneu a executar-lo",
"status": [
"NO ESTÀ INSTAL · LAT",
"CÓRRER",
"NO CORRE"
],
"statusDescription": "Estat actual de l'agent",
"description": "Feu clic als botons següents per instal·lar o desinstal·lar aquest programari de gestió remota. Quan s'instal·la, aquest programari s'executa en segon pla i permet que aquest ordinador sigui gestionat i controlat per un administrador remot."
},
"uk": {
"agent": "Агент",
"agentVersion": "Нова Версія",
"group": "Група Пристроїв",
"url": "URL Сервера",
"meshName": "Ім'я Групи",
"meshId": "Ідентифікатор групи",
"serverId": "Ідентифікатор серверу",
"setup": "Налаштувати",
"update": "Оновлення",
"install": "Інсталювати",
"uninstall": "Видалити",
"connect": "Підключитися",
"disconnect": "Відключити",
"cancel": "Скасувати",
"close": "Закрити",
"pressok": "Натисніть OK, щоб від'єднатися",
"elevation": "Для інсталяції/деінсталяції цього програмного забезпечення потрібні підвищені дозволи.",
"sudo": "Будь ласка, спробуйте ще раз за допомогою sudo.",
"ctrlc": "Натисніть Ctrl-C, щоб вийти",
"commands": "Ви можете запустити текстову версію з командного рядка за допомогою таких команд",
"graphicalerror": "Графічна версія цього інсталятора не може працювати в цій системі",
"zenity": "Спробуйте встановити/оновити Zenity та запустіть наново",
"status": [
"НЕ ВСТАНОВЛЕНО",
"ВИКОНУЄТЬСЯ",
"НЕ ПРАЦЮЄ"
],
"statusDescription": "Поточний Статус Агента",
"description": "Клікнути кнопки нижче, щоб інсталювати або видалити це програмне забезпечення для віддаленого керування. Після інсталювання ця програма працює у фоновому режимі, що дозволяє віддаленому адміністратору керувати цим комп'ютером."
}
}

View file

@ -1,9 +1,20 @@
@ECHO OFF
MD modules_meshcmd_min
MD modules_meshcore_min
"..\..\WebSiteCompiler\bin\Debug\WebSiteCompiler.exe" compressalljs "modules_meshcore" "modules_meshcore_min"
"..\..\WebSiteCompiler\bin\Debug\WebSiteCompiler.exe" compressalljs "modules_meshcmd" "modules_meshcmd_min"
"..\..\WebSiteCompiler\bin\Debug\WebSiteCompiler.exe" meshcore.js
"..\..\WebSiteCompiler\bin\Debug\WebSiteCompiler.exe" meshcmd.js
%LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minifydir C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcore C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcore_min
%LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minifydir C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcmd C:\Users\Default.DESKTOP-9CGK2DI\Desktop\AmtWebApp\meshcentral\agents\modules_meshcmd_min
REM del meshcore.min.js
REM %LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minify meshcore.js
REM rename meshcore.js.min meshcore.min.js
REM del meshcmd.min.js
REM %LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minify meshcmd.js
REM rename meshcmd.js.min meshcmd.min.js
REM Minify the translations
%LOCALAPPDATA%\..\Roaming\nvm\v14.16.0\node ..\translate\translate.js minify modules_meshcore\coretranslations.json
COPY modules_meshcore\coretranslations.json.min modules_meshcore_min\coretranslations.json
DEL modules_meshcore\coretranslations.json.min

View file

@ -1 +1 @@
MeshService.exe hashagents.js > hashagents.json
MeshService-signed.exe hashagents.js > hashagents.json

View file

@ -1,8 +1,8 @@
var fs = require('fs');
var agents = {
'MeshService.exe': 3,
'MeshService64.exe': 4,
'MeshService-signed.exe': 3,
'MeshService64-signed.exe': 4,
'meshagent_x86': 5,
'meshagent_x86-64': 6,
'meshagent_arm': 9,

View file

@ -1,134 +0,0 @@
{
"3": {
"filename": "MeshService.exe",
"hash": "33AE44E73CA79EDD443661F8D6205DF59DE7D03B0AC730A37D283C9CE4079E6136FFC30BC1B79DA8FB05F03CBDE75D06",
"size": 3793408,
"mtime": "2022-08-25T17:55:54Z"
},
"4": {
"filename": "MeshService64.exe",
"hash": "C809BAB1F0B988F1436E1033D9F07A782412A6CC7ECCF4AC52CCCBD91D7B56D401F2AB5FABC71A66F91B20E6FCA393D4",
"size": 3422720,
"mtime": "2022-08-25T17:55:24Z"
},
"5": {
"filename": "meshagent_x86",
"hash": "024A8FCE66C277CFAA375B6F5A12E18D08BF2F8EE494C4408544D93F219F7208BACF056F79A2340428C3C34F765E325E",
"size": 3666464,
"mtime": "2022-08-29T17:48:58Z"
},
"6": {
"filename": "meshagent_x86-64",
"hash": "DC5924847AD22C058D1009BE7EDFAFEAF248DEC706C263736B254BA5917D274A21BAE0D025852EC788007EF3688CDC64",
"size": 3741136,
"mtime": "2022-08-29T17:49:06Z"
},
"7": {
"filename": "meshagent_mips",
"hash": "C49212CA4BF2D1F031F376C0157A4B9C5EA5ACD08180662F27F2EA54F990C2A7840B5A3BF7F66D85EE194EF675008D09",
"size": 4547696,
"mtime": "2022-08-29T17:49:13Z"
},
"9": {
"filename": "meshagent_arm",
"hash": "5217EBF6638EDC64FFFBE3B53BF9DC640D630CC69B9CE484C1CA274530C248D248AC4F4E84071A34CD504039D8D0B022",
"size": 3148064,
"mtime": "2022-08-29T17:49:22Z"
},
"13": {
"filename": "meshagent_pogo",
"hash": "1523191069F30678C607E32F557CD5F9125A963C671CE7A7F6FB8ADD9B9BFB890AC1A6248872EAE899737F378F54FFF2",
"size": 3156744,
"mtime": "2022-08-29T17:49:32Z"
},
"15": {
"filename": "meshagent_poky",
"hash": "36090B49C98D7A3E7515EC2D22E5C47A4FD9BA35B517949BAF04B39A7CE91378656A7F3FC132C5E43FD1D087B3C9226E",
"size": 3796600,
"mtime": "2022-08-29T17:49:42Z"
},
"16": {
"filename": "meshagent_osx-x86-64",
"hash": "F7A3EBEC3D855EBFB2C72271C17196C7692EB2685DCBA70B56B63C80D6CF0DAA7DF00657BB4A12F4C0D92281B1BB47FE",
"size": 4670736,
"mtime": "2022-08-23T03:31:00Z"
},
"18": {
"filename": "meshagent_poky64",
"hash": "FD61B913D2239621FDCC2E949BF16FCAE3F9D46D25EEF74DA0A7971F30A44E315A4231AF824241940391A3F112794A27",
"size": 3495416,
"mtime": "2022-08-29T17:49:52Z"
},
"19": {
"filename": "meshagent_x86_nokvm",
"hash": "BF125A52656DFE6665E78AB22ED652F4C65C17624A12BCAC2F0691A255AF208C3E883101266F3E80052F4CFE8602B29B",
"size": 3385732,
"mtime": "2022-08-29T17:50:00Z"
},
"20": {
"filename": "meshagent_x86-64_nokvm",
"hash": "9AB50A5419A2BAFC8DC485C3F24387622689FE3A0C146317CE3EA951F3EE2E4902CCE3878F2098C6EB23A848E510E478",
"size": 3446192,
"mtime": "2022-08-29T17:50:08Z"
},
"24": {
"filename": "meshagent_arm-linaro",
"hash": "DCC5B487A200F9670B33BE603F52088856FB249CC03F5B62D1617CA9A95B55329B120FE4D3FFD72B2E5FE1ADE302CF81",
"size": 2211156,
"mtime": "2022-08-29T17:50:21Z"
},
"25": {
"filename": "meshagent_armhf",
"hash": "1EDCE4E132927B432F60A3D262368B3DF54B012EDD786EAD31139646B5D9168297C32D13C7D822CE5FEF7FE44B65B4A0",
"size": 3181452,
"mtime": "2022-08-23T03:14:16Z"
},
"27": {
"filename": "meshagent_armhf2",
"hash": "0AE840520D3B677B9767EA097F3AA5A1E24212529E688200F43935DB1541AB9FB441EC2C7BA8002D45299B04695FD037",
"size": 2837724,
"mtime": "1985-10-26T08:15:00Z"
},
"28": {
"filename": "meshagent_mips24kc",
"hash": "88A79B78497D1D004E44D02989A3BE3710D3BEE0A129F98579FFAA826FAC6C90CD69B9B218B90377438942BA85DAC81C",
"size": 4181416,
"mtime": "2022-08-23T03:15:24Z"
},
"29": {
"filename": "meshagent_osx-arm-64",
"hash": "CFE022146F2ED61E68F907E57E3704CC7F409D7F2B4D87E64ED6D83C53F41777BCDDCCF42DF8B8BB25CCEC9A93D799C7",
"size": 3945576,
"mtime": "2022-08-23T03:31:00Z"
},
"30": {
"filename": "meshagent_freebsd_x86-64",
"hash": "5C2CDDA2E7AB5068D990FBC725D8D5E3EA2724A0E001C226C0C7BB9F3A46492880BF260B5DD9E733F87EB68BA7494BD6",
"size": 4673560,
"mtime": "2022-08-23T03:31:12Z"
},
"32": {
"filename": "meshagent_aarch64",
"hash": "21C97445FA93C2A42337AD0E336F840A05EC553F8C040F6021C16339567F8A063EB06876D62C2C2924BD9F656434E9DB",
"size": 3248496,
"mtime": "2022-08-23T03:13:02Z"
},
"40": {
"filename": "meshagent_mipsel24kc",
"hash": "A58CF777ACA3E9B3F7C0FC664E5EAC1F95C3FD03ABDE93E2B06547D0BA1C671A9E67F252CACD9BCBD2019561E18ED7DF",
"size": 4177288,
"mtime": "2022-08-23T03:16:32Z"
},
"41": {
"filename": "meshagent_aarch64-cortex-a53",
"hash": "CC84858AD16C644096B87E3686B318F82CD1A39015FF17D93456456F0A51D4A2D4DEC7F6EAA2ABB065D7EAD28CD024A2",
"size": 3076424,
"mtime": "2022-08-23T03:17:42Z"
},
"10005": {
"filename": "meshagent_osx-universal-64",
"hash": "D320CA61D59FD8D76CF681CFE78A94CE37C47DBCAA8B29DF483F42C000EA9B655B5E5909A2AD6699D45D2D7691FF4964",
"size": 8647784,
"mtime": "2022-08-23T03:31:00Z"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -25,6 +25,7 @@ limitations under the License.
//console.displayStreamPipeMessages = 1; // Display stream pipe and un-pipes
//var __gc = setInterval(function () { console.log('GC'); _debugGC() }, 2000); //
var fs = require('fs');
var os = require('os');
var net = require('net');
@ -128,7 +129,6 @@ function run(argv) {
if ((typeof args.id) == 'string') { settings.id = args.id; }
if ((typeof args.username) == 'string') { settings.username = args.username; }
if ((typeof args.password) == 'string') { settings.password = args.password; }
if ((typeof args.passwordhex) == 'string') { settings.password = Buffer.from(args.passwordhex, 'hex').toString(); }
if ((typeof args.url) == 'string') { settings.url = args.url; }
if ((typeof args.type) == 'string') { settings.type = args.type; }
if ((typeof args.user) == 'string') { settings.username = args.user; }
@ -172,12 +172,6 @@ function run(argv) {
if ((argv.length > 1) && (actions.indexOf(argv[1].toUpperCase()) >= 0)) { settings.action = argv[1]; }
if (globalDebugFlags != 0) { console.setInfoLevel(1); }
if (settings.debuglevel > 1) {
if (settings.hostname) { console.log('Hostname HEX: ' + Buffer.from(settings.hostname, 'binary').toString('hex')); }
if (settings.username) { console.log('Username HEX: ' + Buffer.from(settings.username, 'binary').toString('hex')); }
if (settings.password) { console.log('Password HEX: ' + Buffer.from(settings.password, 'binary').toString('hex')); }
}
// Validate meshaction.txt
if (settings.action == null) {
console.log('MeshCentral Command (MeshCmd) ' + meshCmdVersion);
@ -212,7 +206,6 @@ function run(argv) {
console.log(' AmtWake - Intel AMT Wake Alarms.');
console.log(' AmtRPE - Intel AMT Remote Platform Erase.');
console.log(' AmtDDNS - Intel AMT DDNS settings.');
if (console.canonical != null) { console.log(' AmtTerm - Intel AMT Serial-over-LAN terminal.'); }
console.log('\r\nHelp on a specific action using:\r\n');
console.log(' meshcmd help [action]');
exit(0); return;
@ -386,13 +379,15 @@ function run(argv) {
console.log('\r\Required arguments:\r\n');
console.log(' --scan [ip range] The IP address range to perform the scan on.');
} else if (action == 'amtwifi') {
console.log('AmtWifi is used to list, add or delete Intel AMT Wifi configuration. Example usage:\r\n\r\n meshcmd amtwifi --host 1.2.3.4 --user admin --pass mypassword --list');
console.log('AmtWifi is used to get/set Intel AMT Wifi configuration. Example usage:\r\n\r\n meshcmd amtwifi --host 1.2.3.4 --user admin --pass mypassword --list');
console.log('\r\nRequired arguments:\r\n');
console.log(' --host [hostname] The IP address or DNS name of Intel AMT, 127.0.0.1 is default.');
console.log(' --pass [password] The Intel AMT login password.');
console.log(' --[action] Action options are list, add, del.');
console.log('\r\nOptional arguments:\r\n');
console.log(' --user [username] The Intel AMT login username, admin is default.');
console.log(' --tls Specifies that TLS must be used.');
console.log(' --list List Wifi profiles');
console.log(' --add Add new Wifi profile');
console.log(' --name New Wifi profile name');
console.log(' --priority Priority of this profile - default 0');
@ -441,13 +436,6 @@ function run(argv) {
console.log(' --set [disabled/dhcp/enabled] Set the dynamic DNS mode.');
console.log(' --interval [minutes] Set update interval in minutes, default is 1440, minimum is 20.');
console.log(' --ttl [seconds] Set time to live, default is 900.');
} else if ((action == 'amtterm') && (console.canonical != null)) {
console.log('AmtTerm is used to connect to the Serial-over-LAN port. Example usage:\r\n\r\n meshcmd amtterm --host 1.2.3.4 --user admin --pass mypassword');
console.log('\r\nRequired arguments:\r\n');
console.log(' --host [hostname] The IP address or DNS name of Intel AMT, 127.0.0.1 is default.');
console.log(' --pass [password] The Intel AMT login password.');
console.log('\r\nOptional arguments:\r\n');
console.log(' --tls Specifies that TLS must be used.');
} else {
actions.shift();
console.log('Invalid action, usage:\r\n\r\n meshcmd help [action]\r\n\r\nValid actions are: ' + actions.join(', ') + '.');
@ -588,7 +576,7 @@ function run(argv) {
}
amtMei.getProvisioningState(function (result) { if (result) { mestate.ProvisioningState = result; } });
amtMei.getProvisioningMode(function (result) { if (result) { mestate.ProvisioningMode = result; } });
amtMei.getEHBCState(function (result) { if (result) { mestate.ehbc = ((result === true) || (typeof result == 'object') && (result.EHBC === true)); } });
amtMei.getEHBCState(function (result) { mestate.ehbc = ((result === true) || (typeof result == 'object') && (result.EHBC === true)); });
amtMei.getControlMode(function (result) { if (result) { mestate.controlmode = result; } });
amtMei.getMACAddresses(function (result) { if (result) { mestate.mac = result; } });
amtMei.getLanInterfaceSettings(0, function (result) { if (result) { mestate.net0 = result; } });
@ -719,7 +707,7 @@ function run(argv) {
// Start Intel AMT configuration
if ((settings.url == null) || (typeof settings.url != 'string') || (settings.url == '')) { console.log('No MeshCentral server URL specified, use --url [url].'); exit(1); return; }
if ((settings.id == null) || (typeof settings.id != 'string') || (settings.id == '')) { console.log('No device group identifier specified, use --id [identifier].'); exit(1); return; }
settings.id = settings.id.split('\'').join(''); // Remove single quotes.
settings.id = settings.id.replace('\'', ''); // Remove single quote.
debug(1, "Settings: " + JSON.stringify(settings));
configureAmt();
} else if (settings.action == 'amtccm') {
@ -836,11 +824,6 @@ function run(argv) {
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
performAmtFeatureConfig(args);
} else if ((settings.action == 'amtterm') && (console.canonical != null)) {
if (settings.hostname == null) { settings.hostname = '127.0.0.1'; }
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
performAmtTerm(args);
} else if (settings.action == 'amtpower') { // Perform remote Intel AMT power operation
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { console.log('No or invalid \"hostname\" specified, use --hostname [host].'); exit(1); return; }
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
@ -1500,12 +1483,7 @@ function deactivateACMEx() {
amtstack = new amt(wsstack);
amtstack.Get("AMT_SetupAndConfigurationService", function (stack, name, responses, status) {
if (status !== 200) {
if ((responses != null) && (responses.Header != null) && (typeof responses.Header.error == 'string')) {
console.log(responses.Header.error + ', Status: ' + status);
if (status == 600) { console.log('Check that Intel AMT is in ACM mode and that the password is correct.'); }
} else {
console.log('Command not allowed, Status: ' + status);
}
console.log('Command not allowed. Status: ' + status);
exit(1);
} else {
var sacs = responses.Body;
@ -2418,8 +2396,8 @@ function OnMulticastMessage(msg, rinfo) {
// IDER
//
var ider = null;
var iderIdleTimer = null;
ider = null;
iderIdleTimer = null;
// Perform IDER
function performIder() {
@ -2592,7 +2570,6 @@ function performAmtWifiConfig0(state, args) {
}
function performAmtWifiConfig1(stack, name, response, status, args) {
if (status == 600) { console.log('Unable to login, check that Intel AMT password is correct.'); exit(1); return; }
if (status == 200) {
var wifiAuthMethod = { 1: "Other", 2: "Open", 3: "Shared Key", 4: "WPA PSK", 5: "WPA 802.1x", 6: "WPA2 PSK", 7: "WPA2 802.1x", 32768: "WPA3 802.1x" };
var wifiEncMethod = { 1: "Other", 2: "WEP", 3: "TKIP", 4: "CCMP", 5: "None" }
@ -2604,7 +2581,17 @@ function performAmtWifiConfig1(stack, name, response, status, args) {
}
if (args) {
if (args.add) {
if (args.list) {
console.log('List of Intel AMT Wifi profiles:');
if (wifiProfiles.length == 0) {
console.log('No Wifi profiles is stored.');
}
for (var t in wifiProfiles) {
var w = wifiProfiles[t];
console.log('Profile Name: ' + t + '; Priority: ' + w['Priority'] + '; SSID: ' + w['SSID'] + '; Security: ' + wifiAuthMethod[w['AuthenticationMethod']] + '/' + wifiEncMethod[w['EncryptionMethod']]);
}
exit(0);
} else if (args.add) {
if (args.auth == null) { args.auth = 6; } // if not set, default to WPA2 PSK
if (args.enc == null) { args.enc = 3; } // if not set, default to TKIP
if (args.priority == null) { args.priority = 0; } // if not set, default to 0
@ -2650,16 +2637,6 @@ function performAmtWifiConfig1(stack, name, response, status, args) {
exit(0);
},
0, 1);
} else {
console.log('List of Intel AMT Wifi profiles:');
var wikiProfileCount = 0;
for (var t in wifiProfiles) {
var w = wifiProfiles[t];
console.log('Profile Name: ' + t + '; Priority: ' + w['Priority'] + '; SSID: ' + w['SSID'] + '; Security: ' + wifiAuthMethod[w['AuthenticationMethod']] + '/' + wifiEncMethod[w['EncryptionMethod']]);
wikiProfileCount++;
}
if (wikiProfileCount == 0) { console.log(' (No Wifi profiles stored)'); }
exit(0);
}
} else {
exit(0);
@ -2993,51 +2970,6 @@ function makeUefiBootParam(type, data, len) {
function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
//
// Intel AMT Serial-over-LAN
//
var sol = null;
// Called to start serial-over-lan terminal
function performAmtTerm(args) {
try {
sol = require('amt-redir-duk')(require('amt-sol')());
sol.onStateChanged = onSolStateChange;
sol.m.onData = onSolData;
sol.m.debug = (settings.debuglevel > 0);
sol.Start(settings.hostname, (settings.tls == true) ? 16995 : 16994, settings.username ? 'admin' : settings.username, settings.password, settings.tls);
} catch (ex) { console.log(ex); }
}
// Called when the serial-over-lan connection state changes
function onSolStateChange(stack, state) {
console.log(["Disconnected", "Connecting...", "Connected...", "Started Serial-over-LAN..."][state]);
if (state == 0)
{
console.echo = true; // This enables local echo to the console when keys are pressed
console.canonical = true; // This puts the console into canonical mode, which means stdin will not process each key press individually, instead it will process line by line.
exit(0);
}
if (state == 3)
{
console.echo = false; // This disables local echo to the console when keys are pressed
console.canonical = false; // This takes the console out of canonical mode, which means stdin will process each key press individually, instead of by line.
process.stdin.on('data', function (c) { sol.m.Send(c); });
}
else
{
// Serial-over-LAN is not active, stop any stdin key capture
console.echo = true; // This enables local echo to the console when keys are pressed
console.canonical = true; // This puts the console into canonical mode, which means stdin will not process each key press individually, instead it will process line by line.
}
}
// This is called when serial-over-lan data come in from Intel AMT
function onSolData(stack, data) { process.stdout.write(data); }
//
// Intel AMT feature configuration action
//

File diff suppressed because it is too large Load diff

View file

@ -103,13 +103,14 @@ if (msh.agentName) { connectArgs.push('--agentName="' + msh.agentName + '"'); }
function _install(parms)
{
var i;
var mstr = require('fs').createWriteStream(process.execPath + '.msh', { flags: 'wb' });
for (i in msh)
{
mstr.write(i + '=' + msh[i] + '\n');
}
mstr.write('MeshName=' + msh.MeshName + '\n');
mstr.write('MeshType=' + msh.MeshType + '\n');
mstr.write('MeshID=' + msh.MeshID + '\n');
mstr.write('ServerID=' + msh.ServerID + '\n');
mstr.write('MeshServer=' + msh.MeshServer + '\n');
if (msh.agentName) { mstr.write('agentName=' + msh.agentName + '\n'); }
if (msh.meshServiceName) { mstr.write('meshServiceName=' + msh.meshServiceName + '\n'); }
mstr.end();
if (parms == null) { parms = []; }
@ -155,7 +156,7 @@ if (process.argv.includes('-translations'))
console.log(JSON.stringify(translation));
process.exit();
}
if (process.argv.includes('-help') || (process.platform == 'linux' && process.env['XAUTHORITY'] == null && process.env['DISPLAY'] == null && process.argv.length == 1))
if (process.argv.includes('-help'))
{
console.log("\n" + translation[lang].commands + ": ");
if ((msh.InstallFlags & 1) == 1)
@ -220,7 +221,9 @@ if ((!skip) && ((msh.InstallFlags & 2) == 2))
}
}
if (!skip)
if (!skip)
{
if (process.platform != 'darwin')
{
if (process.argv.includes('-install') || process.argv.includes('-update'))
{
@ -231,10 +234,6 @@ if ((!skip) && ((msh.InstallFlags & 2) == 2))
{
p.push('--installPath="' + process.argv[i].split('=').pop() + '"');
}
else if(process.argv[i].startsWith('--'))
{
p.push(process.argv[i]);
}
}
_install(p);
process.exit();
@ -272,11 +271,12 @@ if ((!skip) && ((msh.InstallFlags & 2) == 2))
process.exit();
}
}
if (process.platform == 'darwin')
{
if (!require('user-sessions').isRoot()) { console.log('\n' + translation[lang].elevation); process.exit(); }
}
}
else
{
if (!require('user-sessions').isRoot()) { console.log('\n' + translation[lang].elevation); process.exit(); }
}
}
if (!skip)

View file

@ -57,9 +57,6 @@ module.exports = function CreateAmtRemoteIder() {
var IDE_ModeSence_Ls120Error_Recovery_Array = new Buffer([0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
var IDE_ModeSence_CDError_Recovery_Array = new Buffer([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00]);
// CD info and performance
var RD_CD_DiskInfo = new Buffer([0x00, 0x20, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x20, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
var RD_CD_Performance = new Buffer([0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00]);
// Private method, called by parent when it change state
obj.xxStateChange = function (newstate) {
@ -98,7 +95,7 @@ module.exports = function CreateAmtRemoteIder() {
// Private method
obj.ProcessData = function (data) {
obj.bytesFromAmt += data.length;
if (obj.acc == null) { obj.acc = data; } else { obj.acc = Buffer.concat([obj.acc, data]); }
if (obj.acc == null) { obj.acc = data; } else { obj.acc = Buffer.concat(obj.acc, data); }
if (obj.debug) console.log('IDER-ProcessData', obj.acc.length, obj.acc.toString('hex'));
// Process as many commands as possible
@ -531,12 +528,6 @@ module.exports = function CreateAmtRemoteIder() {
obj.SendDataToHost(dev, true, r, featureRegister & 1);
}
break;
case 0x51: // READ_DISK_INFORMATION
obj.SendDataToHost(dev, true, RD_CD_DiskInfo, featureRegister & 1);
break;
case 0xAC: // GET_PERFORMANCE
obj.SendDataToHost(dev, true, RD_CD_Performance, featureRegister & 1);
break;
default: // UNKNOWN COMMAND
if (obj.debug) console.log("IDER: Unknown SCSI command", cdb[0]);
obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);

View file

@ -21,10 +21,10 @@ module.exports = function CreateAmtRedirect(module) {
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
obj.xtlsoptions = null;
obj.amtaccumulator = Buffer.alloc(0);
obj.amtaccumulator = null;
obj.amtsequence = 1;
obj.amtkeepalivetimer = null;
obj.authuri = '/RedirectionService';
obj.authuri = "/RedirectionService";
obj.digestRealmMatch = null;
obj.onStateChanged = null;
@ -80,7 +80,7 @@ module.exports = function CreateAmtRedirect(module) {
}
*/
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CONNECTED'); }
if (urlvars && urlvars['redirtrace']) { console.log("REDIR-CONNECTED"); }
//obj.Debug("Socket Connected");
obj.xxStateChange(2);
if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
@ -89,14 +89,14 @@ module.exports = function CreateAmtRedirect(module) {
}
obj.xxOnSocketData = function (data) {
//console.log('xxOnSocketData: ' + data.toString('hex'), data.length);
if (!data || obj.connectstate == -1) return;
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-RECV(' + data.length + '): ' + data.toString('hex')); }
if (urlvars && urlvars['redirtrace']) { console.log("REDIR-RECV(" + data.length + "): " + data.toString('hex')); }
//obj.Debug("Recv(" + data.length + "): " + rstr2hex(data));
if ((obj.protocol == 2 || obj.protocol == 3) && obj.connectstate == 1) { return obj.m.ProcessData(data); } // KVM or IDER traffic, forward it directly.
obj.amtaccumulator = Buffer.concat([obj.amtaccumulator, data]);
//obj.Debug("Recv(" + obj.amtaccumulator.length + "): " + obj.amtaccumulator.toString('hex'));
while (obj.amtaccumulator.length > 0) {
if (obj.amtaccumulator == null) { obj.amtaccumulator = data; } else { obj.amtaccumulator = Buffer.concat(obj.amtaccumulator, data); }
//obj.Debug("Recv(" + obj.amtaccumulator.length + "): " + rstr2hex(obj.amtaccumulator));
while (obj.amtaccumulator != null) {
var cmdsize = 0;
//console.log('CMD: ' + obj.amtaccumulator[0]);
switch (obj.amtaccumulator[0]) {
@ -170,9 +170,9 @@ module.exports = function CreateAmtRedirect(module) {
qoplen = authDataBuf[curptr];
qop = authDataBuf.slice(curptr + 1, curptr + 1 + qoplen).toString();
curptr += (qoplen + 1);
extra = snc + ':' + cnonce + ':' + qop + ':';
extra = snc + ":" + cnonce + ":" + qop + ":";
}
var digest = hex_md5(hex_md5(obj.user + ':' + realm + ':' + obj.pass) + ':' + nonce + ':' + extra + hex_md5('POST:' + obj.authuri));
var digest = hex_md5(hex_md5(obj.user + ":" + realm + ":" + obj.pass) + ":" + nonce + ":" + extra + hex_md5("POST:" + obj.authuri));
var totallen = obj.user.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
if (authType == 4) totallen += (qop.length + 1);
var buf = Buffer.concat([new Buffer([0x13, 0x00, 0x00, 0x00, authType]), new Buffer([totallen & 0xFF, (totallen >> 8) & 0xFF, 0x00, 0x00]), new Buffer([obj.user.length]), new Buffer(obj.user), new Buffer([realm.length]), new Buffer(realm), new Buffer([nonce.length]), new Buffer(nonce), new Buffer([obj.authuri.length]), new Buffer(obj.authuri), new Buffer([cnonce.length]), new Buffer(cnonce), new Buffer([snc.length]), new Buffer(snc), new Buffer([digest.length]), new Buffer(digest)]);
@ -181,6 +181,7 @@ module.exports = function CreateAmtRedirect(module) {
}
else if (status == 0) { // Success
if (obj.protocol == 1) {
/*
// Serial-over-LAN: Send Intel AMT serial settings...
var MaxTxBuffer = 10000;
var TxTimeout = 100;
@ -189,6 +190,7 @@ module.exports = function CreateAmtRedirect(module) {
var RxFlushTimeout = 100;
var Heartbeat = 0;//5000;
obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(MaxTxBuffer) + ToShortStr(TxTimeout) + ToShortStr(TxOverflowTimeout) + ToShortStr(RxTimeout) + ToShortStr(RxFlushTimeout) + ToShortStr(Heartbeat) + ToIntStr(0));
*/
}
if (obj.protocol == 2) {
// Remote Desktop: Send traffic directly...
@ -217,7 +219,7 @@ module.exports = function CreateAmtRedirect(module) {
if (obj.amtaccumulator.length < 10) break;
var cs = (10 + ((obj.amtaccumulator[9] & 0xFF) << 8) + (obj.amtaccumulator[8] & 0xFF));
if (obj.amtaccumulator.length < cs) break;
obj.m.ProcessData(obj.amtaccumulator.slice(10, cs));
obj.m.ProcessData(obj.amtaccumulator.substring(10, cs));
cmdsize = cs;
break;
case 0x2B: // Keep alive message (43)
@ -233,22 +235,21 @@ module.exports = function CreateAmtRedirect(module) {
cmdsize = obj.amtaccumulator.length;
break;
default:
console.log('Unknown Intel AMT command: ' + obj.amtaccumulator[0] + ' acclen=' + obj.amtaccumulator.length);
console.log("Unknown Intel AMT command: " + obj.amtaccumulator[0] + " acclen=" + obj.amtaccumulator.length);
obj.Stop();
return;
}
if (cmdsize == 0) return;
obj.amtaccumulator = obj.amtaccumulator.slice(cmdsize);
if (cmdsize == obj.amtaccumulator.length) { obj.amtaccumulator = null; } else { obj.amtaccumulator = obj.amtaccumulator.slice(cmdsize); }
}
}
obj.xxSend = function (x) {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-SEND(' + x.length + '): ' + rstr2hex(x)); }
//obj.Debug('Send(' + x.length + '): ' + Buffer.from(x, 'binary').toString('hex'));
if (typeof x == 'string') { obj.socket.write(Buffer.from(x, 'binary')); } else { obj.socket.write(x); }
if (urlvars && urlvars['redirtrace']) { console.log("REDIR-SEND(" + x.length + "): " + rstr2hex(x)); }
//obj.Debug("Send(" + x.length + "): " + Buffer.from(x, "binary").toString('hex'));
if (typeof x == 'string') { obj.socket.write(Buffer.from(x, "binary")); } else { obj.socket.write(x); }
}
// Send Serial-over-LAN ASCII characters
obj.Send = function (x) {
if (obj.socket == null || obj.connectstate != 1) return;
if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(x.length) + x); } else { obj.xxSend(x); }
@ -262,14 +263,14 @@ module.exports = function CreateAmtRedirect(module) {
// Uses OpenSSL random to generate a hex string
obj.xxRandomValueHex = function (len) {
var t = [], l = Math.floor(len / 2);
for (var i = 0; i < l; i++) { t.push(obj.tls.generateRandomInteger('0', '255')); }
for (var i = 0; i < l; i++) { t.push(obj.tls.generateRandomInteger("0", "255")); }
return new Buffer(t).toString('hex');
}
obj.xxOnSocketClosed = function () {
obj.socket = null;
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
//obj.Debug('Socket Closed');
if (urlvars && urlvars['redirtrace']) { console.log("REDIR-CLOSED"); }
//obj.Debug("Socket Closed");
obj.Stop();
}
@ -281,11 +282,11 @@ module.exports = function CreateAmtRedirect(module) {
}
obj.Stop = function () {
if (urlvars && urlvars['redirtrace']) { console.log('REDIR-CLOSED'); }
//obj.Debug('Socket Stopped');
if (urlvars && urlvars['redirtrace']) { console.log("REDIR-CLOSED"); }
//obj.Debug("Socket Stopped");
obj.xxStateChange(0);
obj.connectstate = -1;
obj.amtaccumulator = Buffer.alloc(0);
obj.amtaccumulator = "";
if (obj.socket != null) { obj.socket.destroy(); obj.socket = null; }
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
}

View file

@ -1,20 +0,0 @@
/**
* @description Serial-over-LAN Handling Module
* @author Ylian Saint-Hilaire
*/
// meshservice meshcmd.js amtterm --host 192.168.2.186 --pass P@ssw0rd
// Construct a Intel AMT Serial-over-LAN object
module.exports = function CreateAmtRemoteSol() {
var obj = {};
obj.protocol = 1; // Serial-over-LAN
obj.debug = false;
obj.onData = null;
obj.xxStateChange = function (newstate) { if (obj.debug) console.log('SOL-StateChange', newstate); if (newstate == 0) { obj.Stop(); } if (newstate == 3) { obj.Start(); } }
obj.Start = function () { if (obj.debug) { console.log('SOL-Start'); } }
obj.Stop = function () { if (obj.debug) { console.log('SOL-Stop'); } }
obj.ProcessData = function (data) { if (obj.debug) { console.log('SOL-ProcessData', data); } if (obj.onData) { obj.onData(obj, data); } }
obj.Send = function(text) { if (obj.debug) { console.log('SOL-Send', text); } obj.parent.Send(text); }
return obj;
}

View file

@ -86,7 +86,7 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/) {
//console.log('Request ' + (obj.RequestCount++));
if (globalDebugFlags & 1) { console.log('Request ' + (obj.RequestCount++)); } // DEBUG
req.on('error', function (err) { obj.gotNextMessagesError({ status: 600, error: '' + err }, 'error', null, [postdata, callback, tag]); });
req.on('error', function (e) { obj.gotNextMessagesError({ status: 600 }, 'error', null, [postdata, callback, tag]); });
req.on('response', function (response) {
//console.log(JSON.stringify(response, null, 2));
if (globalDebugFlags & 1) { console.log('Response: ' + response.statusCode); }
@ -126,7 +126,7 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/) {
if (obj.FailAllError == 999) return;
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
//if (status != 200) { console.log("ERROR, status=" + status + "\r\n\r\nreq=" + callArgs[0]); } // Debug: Display the request & response if something did not work.
if (obj.FailAllError != 999) { callArgs[1]({ Header: { HttpError: request.status, error: request.error } }, request.status, callArgs[2]); }
if (obj.FailAllError != 999) { callArgs[1]({ Header: { HttpError: request.status } }, request.status, callArgs[2]); }
obj.PerformNextAjax();
}

View file

@ -43,7 +43,7 @@ function WsmanStackCreateService(/*CreateWsmanComm, host, port, user, pass, tls,
obj.PerformAjax = function PerformAjax(postdata, callback, tag, pri, namespaces) {
if (namespaces == null) namespaces = '';
obj.comm.PerformAjax('<?xml version=\"1.0\" encoding=\"utf-8\"?><Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns=\"http://www.w3.org/2003/05/soap-envelope\" ' + namespaces + '><Header><a:Action>' + postdata, function (data, status, tag) {
if (status != 200) { callback(obj, null, (data != null) ? data : { Header: { HttpError: status } }, status, tag); return; }
if (status != 200) { callback(obj, null, { Header: { HttpError: status } }, status, tag); return; }
var wsresponse = obj.xmlParser.ParseWsman(data);
if (!wsresponse || wsresponse == null) { callback(obj, null, { Header: { HttpError: status } }, 601, tag); } else { callback(obj, wsresponse.Header["ResourceURI"], wsresponse, 200, tag); }
}, tag, pri);

View file

@ -514,98 +514,60 @@ function AmtStackCreateService(wsmanStack) {
var _SystemEntityTypes = "Unspecified|Other|Unknown|Processor|Disk|Peripheral|System management module|System board|Memory module|Processor module|Power supply|Add in card|Front panel board|Back panel board|Power system board|Drive backplane|System internal expansion board|Other system board|Processor board|Power unit|Power module|Power management board|Chassis back panel board|System chassis|Sub chassis|Other chassis board|Disk drive bay|Peripheral bay|Device bay|Fan cooling|Cooling unit|Cable interconnect|Memory device|System management software|BIOS|Intel(r) ME|System bus|Group|Intel(r) ME|External environment|Battery|Processing blade|Connectivity switch|Processor/memory module|I/O module|Processor I/O module|Management controller firmware|IPMI channel|PCI bus|PCI express bus|SCSI bus|SATA/SAS bus|Processor front side bus".split('|');
obj.RealmNames = "||Redirection|PT Administration|Hardware Asset|Remote Control|Storage|Event Manager|Storage Admin|Agent Presence Local|Agent Presence Remote|Circuit Breaker|Network Time|General Information|Firmware Update|EIT|LocalUN|Endpoint Access Control|Endpoint Access Control Admin|Event Log Reader|Audit Log|ACL Realm|||Local System".split('|');
obj.WatchdogCurrentStates = { 1: 'Not Started', 2: 'Stopped', 4: 'Running', 8: 'Expired', 16: 'Suspended' };
var _OCRProgressEvents = ["Boot parameters received from CSME", "CSME Boot Option % added successfully", "HTTPS URI name resolved", "HTTPS connected successfully", "HTTPSBoot download is completed", "Attempt to boot", "Exit boot services"];
var _OCRErrorEvents = ['', "No network connection available", "Name resolution of URI failed", "Connect to URI failed", "OEM app not found at local URI", "HTTPS TLS Auth failed", "HTTPS Digest Auth failed", "Verified boot failed (bad image)", "HTTPS Boot File not found"];
var _OCRSource = { 1: '', 2: "HTTPS", 4: "Local PBA", 8: "WinRE" };
function _GetEventDetailStr(eventSensorType, eventOffset, eventDataField, entity) {
if (eventSensorType == 15) {
if (eventSensorType == 15)
{
if (eventDataField[0] == 235) return "Invalid Data";
if (eventOffset == 0) {
return _SystemFirmwareError[eventDataField[1]];
} else if (eventOffset == 3) {
if ((eventDataField[0] == 170) && (eventDataField[1] == 48)) {
return format("One Click Recovery: {0}", _OCRErrorEvents[eventDataField[2]]);
} else if ((eventDataField[0] == 170) && (eventDataField[1] == 64)) {
if (eventDataField[2] == 1) return "Got an error erasing Device SSD";
if (eventDataField[2] == 2) return "Erasing Device TPM is not supported";
if (eventDataField[2] == 3) return "Reached Max Counter";
} else {
return "OEM Specific Firmware Error event";
}
} else if (eventOffset == 5) {
if ((eventDataField[0] == 170) && (eventDataField[1] == 48)) {
if (eventDataField[2] == 1) {
return format("One Click Recovery: CSME Boot Option {0}:{1} added successfully", (eventDataField[3]), _OCRSource[(eventDataField[3])]);
} else if (eventDataField[2] < 7) {
return format("One Click Recovery: {0}", _OCRProgressEvents[eventDataField[2]]);
} else {
return format("One Click Recovery: Unknown progress event {0}", eventDataField[2]);
}
} else if ((eventDataField[0] == 170) && (eventDataField[1] == 64)) {
if (eventDataField[2] == 1) {
if (eventDataField[3] == 2) return "Started erasing Device SSD";
if (eventDataField[3] == 3) return "Started erasing Device TPM";
if (eventDataField[3] == 5) return "Started erasing Device BIOS Reload of Golden Config";
}
if (eventDataField[2] == 2) {
if (eventDataField[3] == 2) return "Erasing Device SSD ended successfully";
if (eventDataField[3] == 3) return "Erasing Device TPM ended successfully";
if (eventDataField[3] == 5) return "Erasing Device BIOS Reload of Golden Config ended successfully";
}
if (eventDataField[2] == 3) return "Beginning Platform Erase";
if (eventDataField[2] == 4) return "Clear Reserved Parameters";
if (eventDataField[2] == 5) return "All setting decremented";
} else {
return "OEM Specific Firmware Progress event";
}
} else {
return _SystemFirmwareProgress[eventDataField[1]];
}
if (eventOffset == 0) return _SystemFirmwareError[eventDataField[1]];
return _SystemFirmwareProgress[eventDataField[1]];
}
if ((eventSensorType == 18) && (eventDataField[0] == 170)) // System watchdog event
if (eventSensorType == 18 && eventDataField[0] == 170) // System watchdog event
{
return "Agent watchdog " + char2hex(eventDataField[4]) + char2hex(eventDataField[3]) + char2hex(eventDataField[2]) + char2hex(eventDataField[1]) + "-" + char2hex(eventDataField[6]) + char2hex(eventDataField[5]) + "-... changed to " + obj.WatchdogCurrentStates[eventDataField[7]];
}
if ((eventSensorType == 5) && (eventOffset == 0)) // System chassis
{
return "Case intrusion";
}
//if (eventSensorType == 5 && eventOffset == 0) // System chassis
//{
// return "Case intrusion";
//}
if ((eventSensorType == 192) && (eventOffset == 0) && (eventDataField[0] == 170) && (eventDataField[1] == 48))
{
if (eventDataField[2] == 0) return "A remote Serial Over LAN session was established.";
if (eventDataField[2] == 1) return "Remote Serial Over LAN session finished. User control was restored.";
if (eventDataField[2] == 2) return "A remote IDE-Redirection session was established.";
if (eventDataField[2] == 3) return "Remote IDE-Redirection session finished. User control was restored.";
}
//if (eventSensorType == 192 && eventOffset == 0 && eventDataField[0] == 170 && eventDataField[1] == 48)
//{
// if (eventDataField[2] == 0) return "A remote Serial Over LAN session was established.";
// if (eventDataField[2] == 1) return "Remote Serial Over LAN session finished. User control was restored.";
// if (eventDataField[2] == 2) return "A remote IDE-Redirection session was established.";
// if (eventDataField[2] == 3) return "Remote IDE-Redirection session finished. User control was restored.";
//}
if (eventSensorType == 36)
{
var handle = (eventDataField[1] << 24) + (eventDataField[2] << 16) + (eventDataField[3] << 8) + eventDataField[4];
var nic = '#' + eventDataField[0];
if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless *****
//if (eventDataField[0] == 0xAA) nic = "wireless";
//if (eventSensorType == 36)
//{
// long handle = ((long)(eventDataField[1]) << 24) + ((long)(eventDataField[2]) << 16) + ((long)(eventDataField[3]) << 8) + (long)(eventDataField[4]);
// string nic = string.Format("#{0}", eventDataField[0]);
// if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless *****
// //if (eventDataField[0] == 0xAA) nic = "wireless";
if (handle == 4294967293) { return "All received packet filter was matched on " + nic + " interface."; }
if (handle == 4294967292) { return "All outbound packet filter was matched on " + nic + " interface."; }
if (handle == 4294967290) { return "Spoofed packet filter was matched on " + nic + " interface."; }
return "Filter " + handle + " was matched on " + nic + " interface.";
}
// if (handle == 4294967293) { return string.Format("All received packet filter was matched on {0} interface.", nic); }
// if (handle == 4294967292) { return string.Format("All outbound packet filter was matched on {0} interface.", nic); }
// if (handle == 4294967290) { return string.Format("Spoofed packet filter was matched on {0} interface.", nic); }
// return string.Format("Filter {0} was matched on {1} interface.", handle, nic);
//}
if (eventSensorType == 192) {
if (eventDataField[2] == 0) return "Security policy invoked. Some or all network traffic (TX) was stopped.";
if (eventDataField[2] == 2) return "Security policy invoked. Some or all network traffic (RX) was stopped.";
return "Security policy invoked.";
}
//if (eventSensorType == 192)
//{
// if (eventDataField[2] == 0) return "Security policy invoked. Some or all network traffic (TX) was stopped.";
// if (eventDataField[2] == 2) return "Security policy invoked. Some or all network traffic (RX) was stopped.";
// return "Security policy invoked.";
//}
if (eventSensorType == 193) {
if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x30) && (eventDataField[2] == 0x00) && (eventDataField[3] == 0x00)) { return "User request for remote connection."; }
if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x20) && (eventDataField[2] == 0x03) && (eventDataField[3] == 0x01)) { return "EAC error: attempt to get posture while NAC in Intel(r) AMT is disabled."; } // eventDataField = 0xAA20030100000000
if ((eventDataField[0] == 0xAA) && (eventDataField[1] == 0x20) && (eventDataField[2] == 0x04) && (eventDataField[3] == 0x00)) { return "Certificate revoked. "; }
}
//if (eventSensorType == 193)
//{
// if (eventDataField[0] == 0xAA && eventDataField[1] == 0x30 && eventDataField[2] == 0x00 && eventDataField[3] == 0x00) { return "User request for remote connection."; }
// if (eventDataField[0] == 0xAA && eventDataField[1] == 0x20 && eventDataField[2] == 0x03 && eventDataField[3] == 0x01) { return "EAC error: attempt to get posture while NAC in Intel(r) AMT is disabled."; // eventDataField = 0xAA20030100000000 }
// if (eventDataField[0] == 0xAA && eventDataField[1] == 0x20 && eventDataField[2] == 0x04 && eventDataField[3] == 0x00) { return "Certificate revoked. "; }
//}
if (eventSensorType == 6) return "Authentication failed " + (eventDataField[1] + (eventDataField[2] << 8)) + " times. The system may be under attack.";
if (eventSensorType == 30) return "No bootable media";
@ -709,12 +671,9 @@ function AmtStackCreateService(wsmanStack) {
2600: 'Agent Watchdog Added',
2601: 'Agent Watchdog Removed',
2602: 'Agent Watchdog Action Set',
2700: "Wireless Profile Added",
2701: "Wireless Profile Removed",
2702: "Wireless Profile Updated",
2703: "An existing profile sync was modified",
2704: "An existing profile link preference was changed",
2705: "Wireless profile share with UEFI enabled setting was changed",
2700: 'Wireless Profile Added',
2701: 'Wireless Profile Removed',
2702: 'Wireless Profile Updated',
2800: 'EAC Posture Signer SET',
2801: 'EAC Enabled',
2802: 'EAC Disabled',
@ -813,6 +772,7 @@ function AmtStackCreateService(wsmanStack) {
// Read network access
x['MCLocationType'] = e[ptr++];
var netlen = e[ptr++];
x['NetAddress'] = e.slice(ptr, ptr + netlen).toString();
// Read extended data
@ -1027,8 +987,8 @@ function AmtStackCreateService(wsmanStack) {
// Convert a byte array of SID into string
function GetSidString(sid) {
var r = 'S-' + sid[0] + '-' + sid[7];
for (var i = 2; i < (sid.length / 4); i++) r += '-' + ReadIntX(sid, i * 4);
var r = "S-" + sid.charCodeAt(0) + "-" + sid.charCodeAt(7);
for (var i = 2; i < (sid.length / 4); i++) r += "-" + ReadIntX(sid, i * 4);
return r;
}

View file

@ -269,18 +269,15 @@ function SMBiosTables()
this.amtInfo = function amtInfo(data) {
if (!data) { throw ('no data'); }
var retVal = { AMT: false };
if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT')
{
if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') {
var amt = data[130].peek();
retVal.AMT = amt[4] ? true : false;
if (retVal.AMT)
{
if (retVal.AMT) {
retVal.enabled = amt[5] ? true : false;
retVal.storageRedirection = amt[6] ? true : false;
retVal.serialOverLan = amt[7] ? true : false;
retVal.kvm = amt[14] ? true : false;
if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro')
{
if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') {
var settings = data[131].peek();
if (settings[0] & 0x04) { retVal.TXT = (settings[0] & 0x08) ? true : false; }
if (settings[0] & 0x10) { retVal.VMX = (settings[0] & 0x20) ? true : false; }
@ -298,14 +295,6 @@ function SMBiosTables()
}
}
}
if (!retVal.AMT)
{
if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro')
{
var settings = data[131].peek();
if ((settings[20] & 0x08) == 0x08) { retVal.AMT = true; }
}
}
return (retVal);
};
this.smTableTypes = {

View file

@ -225,14 +225,19 @@ function macos_memUtilization()
function windows_thermals()
{
var ret = [];
try {
ret = require('win-wmi').query('ROOT\\WMI', 'SELECT CurrentTemperature,InstanceName FROM MSAcpi_ThermalZoneTemperature',['CurrentTemperature','InstanceName']);
if (ret[0]) {
for (var i = 0; i < ret.length; ++i) {
ret[i]['CurrentTemperature'] = ((parseFloat(ret[i]['CurrentTemperature']) / 10) - 273.15).toFixed(2);
}
child = require('child_process').execFile(process.env['windir'] + '\\System32\\wbem\\wmic.exe', ['wmic', '/namespace:\\\\root\\wmi', 'PATH', 'MSAcpi_ThermalZoneTemperature', 'get', 'CurrentTemperature']);
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.waitExit();
if(child.stdout.str.trim!='')
{
var lines = child.stdout.str.trim().split('\r\n');
for (var i = 1; i < lines.length; ++i)
{
if (lines[i].trim() != '') { ret.push(((parseFloat(lines[i]) / 10) - 273.15).toFixed(2)); }
}
} catch (ex) { }
}
return (ret);
}
@ -280,10 +285,16 @@ function macos_thermals()
return (ret);
}
const platformConfig = {
linux: { cpuUtilization: linux_cpuUtilization, memUtilization: linux_memUtilization, thermals: linux_thermals },
win32: { cpuUtilization: windows_cpuUtilization, memUtilization: windows_memUtilization, thermals: windows_thermals },
darwin: { cpuUtilization: macos_cpuUtilization, memUtilization: macos_memUtilization, thermals: macos_thermals }
};
switch(process.platform)
{
case 'linux':
module.exports = { cpuUtilization: linux_cpuUtilization, memUtilization: linux_memUtilization, thermals: linux_thermals };
break;
case 'win32':
module.exports = { cpuUtilization: windows_cpuUtilization, memUtilization: windows_memUtilization, thermals: windows_thermals };
break;
case 'darwin':
module.exports = { cpuUtilization: macos_cpuUtilization, memUtilization: macos_memUtilization, thermals: macos_thermals };
break;
}
module.exports = platformConfig[process.platform];

View file

@ -1,902 +0,0 @@
/*
Copyright 2019-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.
*/
function trimIdentifiers(val)
{
for(var v in val)
{
if (!val[v] || val[v] == 'None' || val[v] == '') { delete val[v]; }
}
}
function trimResults(val)
{
var i, x;
for (i = 0; i < val.length; ++i)
{
for (x in val[i])
{
if (x.startsWith('_'))
{
delete val[i][x];
}
else
{
if (val[i][x] == null || val[i][x] == 0)
{
delete val[i][x];
}
}
}
}
}
function brief(headers, obj)
{
var i, x;
for (x = 0; x < obj.length; ++x)
{
for (i in obj[x])
{
if (!headers.includes(i))
{
delete obj[x][i];
}
}
}
return (obj);
}
function dataHandler(c)
{
this.str += c.toString();
}
function linux_identifiers()
{
var identifiers = {};
var ret = {};
var values = {};
if (!require('fs').existsSync('/sys/class/dmi/id')) {
if (require('fs').existsSync('/sys/firmware/devicetree/base/model')) {
if (require('fs').readFileSync('/sys/firmware/devicetree/base/model').toString().trim().startsWith('Raspberry')) {
identifiers['board_vendor'] = 'Raspberry Pi';
identifiers['board_name'] = require('fs').readFileSync('/sys/firmware/devicetree/base/model').toString().trim();
identifiers['board_serial'] = require('fs').readFileSync('/sys/firmware/devicetree/base/serial-number').toString().trim();
const memorySlots = [];
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write('vcgencmd get_mem arm && vcgencmd get_mem gpu\nexit\n');
child.waitExit();
try {
const lines = child.stdout.str.trim().split('\n');
if (lines.length == 2) {
memorySlots.push({ Locator: "ARM Memory", Size: lines[0].split('=')[1].trim() })
memorySlots.push({ Locator: "GPU Memory", Size: lines[1].split('=')[1].trim() })
ret.memory = { Memory_Device: memorySlots };
}
} catch (xx) { }
} else {
throw('Unknown board');
}
} else {
throw ('this platform does not have DMI statistics');
}
} else {
var entries = require('fs').readdirSync('/sys/class/dmi/id');
for (var i in entries) {
if (require('fs').statSync('/sys/class/dmi/id/' + entries[i]).isFile()) {
try {
ret[entries[i]] = require('fs').readFileSync('/sys/class/dmi/id/' + entries[i]).toString().trim();
} catch(z) { }
if (ret[entries[i]] == 'None') { delete ret[entries[i]]; }
}
}
entries = null;
identifiers['bios_date'] = ret['bios_date'];
identifiers['bios_vendor'] = ret['bios_vendor'];
identifiers['bios_version'] = ret['bios_version'];
identifiers['bios_serial'] = ret['product_serial'];
identifiers['board_name'] = ret['board_name'];
identifiers['board_serial'] = ret['board_serial'];
identifiers['board_vendor'] = ret['board_vendor'];
identifiers['board_version'] = ret['board_version'];
identifiers['product_uuid'] = ret['product_uuid'];
identifiers['product_name'] = ret['product_name'];
}
try {
identifiers['bios_mode'] = (require('fs').statSync('/sys/firmware/efi').isDirectory() ? 'UEFI': 'Legacy');
} catch (ex) { identifiers['bios_mode'] = 'Legacy'; }
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write('cat /proc/cpuinfo | grep -i "model name" | ' + "tr '\\n' ':' | awk -F: '{ print $2 }'\nexit\n");
child.waitExit();
identifiers['cpu_name'] = child.stdout.str.trim();
if (identifiers['cpu_name'] == "") { // CPU BLANK, check lscpu instead
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write('lscpu | grep -i "model name" | ' + "tr '\\n' ':' | awk -F: '{ print $2 }'\nexit\n");
child.waitExit();
identifiers['cpu_name'] = child.stdout.str.trim();
}
child = null;
// Fetch GPU info
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write("lspci | grep ' VGA ' | tr '\\n' '`' | awk '{ a=split($0,lines" + ',"`"); printf "["; for(i=1;i<a;++i) { split(lines[i],gpu,"r: "); printf "%s\\"%s\\"", (i==1?"":","),gpu[2]; } printf "]"; }\'\nexit\n');
child.waitExit();
try { identifiers['gpu_name'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
child = null;
// Fetch Storage Info
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write("lshw -class disk | tr '\\n' '`' | awk '" + '{ len=split($0,lines,"*"); printf "["; for(i=2;i<=len;++i) { model=""; caption=""; size=""; clen=split(lines[i],item,"`"); for(j=2;j<clen;++j) { split(item[j],tokens,":"); split(tokens[1],key," "); if(key[1]=="description") { caption=substr(tokens[2],2); } if(key[1]=="product") { model=substr(tokens[2],2); } if(key[1]=="size") { size=substr(tokens[2],2); } } if(model=="") { model=caption; } if(caption!="" || model!="") { printf "%s{\\"Caption\\":\\"%s\\",\\"Model\\":\\"%s\\",\\"Size\\":\\"%s\\"}",(i==2?"":","),caption,model,size; } } printf "]"; }\'\nexit\n');
child.waitExit();
try { identifiers['storage_devices'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
child = null;
// Fetch storage volumes using df
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write('df -T | awk \'NR==1 || $1 ~ ".+"{print $3, $4, $5, $7, $2}\' | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\",\\"type\\":\\"%s\\"},", $1, $2, $3, $4, $5}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
child.waitExit();
try { ret.volumes = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
child = null;
values.identifiers = identifiers;
values.linux = ret;
trimIdentifiers(values.identifiers);
var dmidecode = require('lib-finder').findBinary('dmidecode');
if (dmidecode != null)
{
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stderr.str = ''; child.stderr.on('data', dataHandler);
child.stdin.write(dmidecode + " -t memory | tr '\\n' '`' | ");
child.stdin.write(" awk '{ ");
child.stdin.write(' printf("[");');
child.stdin.write(' comma="";');
child.stdin.write(' c=split($0, lines, "``");');
child.stdin.write(' for(i=1;i<=c;++i)');
child.stdin.write(' {');
child.stdin.write(' d=split(lines[i], val, "`");');
child.stdin.write(' split(val[1], tokens, ",");');
child.stdin.write(' split(tokens[2], dmitype, " ");');
child.stdin.write(' dmi = dmitype[3]+0; ');
child.stdin.write(' if(dmi == 5 || dmi == 6 || dmi == 16 || dmi == 17)');
child.stdin.write(' {');
child.stdin.write(' ccx="";');
child.stdin.write(' printf("%s{\\"%s\\": {", comma, val[2]);');
child.stdin.write(' for(j=3;j<d;++j)');
child.stdin.write(' {');
child.stdin.write(' sub(/^[ \\t]*/,"",val[j]);');
child.stdin.write(' if(split(val[j],tmp,":")>1)');
child.stdin.write(' {');
child.stdin.write(' sub(/^[ \\t]*/,"",tmp[2]);');
child.stdin.write(' gsub(/ /,"",tmp[1]);');
child.stdin.write(' printf("%s\\"%s\\": \\"%s\\"", ccx, tmp[1], tmp[2]);');
child.stdin.write(' ccx=",";');
child.stdin.write(' }');
child.stdin.write(' }');
child.stdin.write(' printf("}}");');
child.stdin.write(' comma=",";');
child.stdin.write(' }');
child.stdin.write(' }');
child.stdin.write(' printf("]");');
child.stdin.write("}'\nexit\n");
child.waitExit();
try
{
var j = JSON.parse(child.stdout.str);
var i, key, key2;
for (i = 0; i < j.length; ++i)
{
for (key in j[i])
{
delete j[i][key]['ArrayHandle'];
delete j[i][key]['ErrorInformationHandle'];
for (key2 in j[i][key])
{
if (j[i][key][key2] == 'Unknown' || j[i][key][key2] == 'Not Specified' || j[i][key][key2] == '')
{
delete j[i][key][key2];
}
}
}
}
if(j.length > 0){
var mem = {};
for (i = 0; i < j.length; ++i)
{
for (key in j[i])
{
if (mem[key] == null) { mem[key] = []; }
mem[key].push(j[i][key]);
}
}
values.linux.memory = mem;
}
}
catch (e)
{ }
child = null;
}
var usbdevices = require('lib-finder').findBinary('usb-devices');
if (usbdevices != null)
{
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stderr.str = ''; child.stderr.on('data', dataHandler);
child.stdin.write(usbdevices + " | tr '\\n' '`' | ");
child.stdin.write(" awk '");
child.stdin.write('{');
child.stdin.write(' comma="";');
child.stdin.write(' printf("[");');
child.stdin.write(' len=split($0, group, "``");');
child.stdin.write(' for(i=1;i<=len;++i)');
child.stdin.write(' {');
child.stdin.write(' comma2="";');
child.stdin.write(' xlen=split(group[i], line, "`");');
child.stdin.write(' scount=0;');
child.stdin.write(' for(x=1;x<xlen;++x)');
child.stdin.write(' {');
child.stdin.write(' if(line[x] ~ "^S:")');
child.stdin.write(' {');
child.stdin.write(' ++scount;');
child.stdin.write(' }');
child.stdin.write(' }');
child.stdin.write(' if(scount>0)');
child.stdin.write(' {');
child.stdin.write(' printf("%s{", comma); comma=",";');
child.stdin.write(' for(x=1;x<xlen;++x)');
child.stdin.write(' {');
child.stdin.write(' if(line[x] ~ "^T:")');
child.stdin.write(' {');
child.stdin.write(' comma3="";');
child.stdin.write(' printf("%s\\"hardware\\": {", comma2); comma2=",";');
child.stdin.write(' sub(/^T:[ \\t]*/, "", line[x]);');
child.stdin.write(' gsub(/= */, "=", line[x]);');
child.stdin.write(' blen=split(line[x], tokens, " ");');
child.stdin.write(' for(y=1;y<blen;++y)');
child.stdin.write(' {');
child.stdin.write(' match(tokens[y],/=/);');
child.stdin.write(' h=substr(tokens[y],1,RSTART-1);');
child.stdin.write(' v=substr(tokens[y],RSTART+1);');
child.stdin.write(' sub(/#/, "", h);');
child.stdin.write(' printf("%s\\"%s\\": \\"%s\\"", comma3, h, v); comma3=",";');
child.stdin.write(' }');
child.stdin.write(' printf("}");');
child.stdin.write(' }');
child.stdin.write(' if(line[x] ~ "^S:")');
child.stdin.write(' {');
child.stdin.write(' sub(/^S:[ \\t]*/, "", line[x]);');
child.stdin.write(' match(line[x], /=/);');
child.stdin.write(' h=substr(line[x],1,RSTART-1);');
child.stdin.write(' v=substr(line[x],RSTART+1);');
child.stdin.write(' printf("%s\\"%s\\": \\"%s\\"", comma2, h,v); comma2=",";');
child.stdin.write(' }');
child.stdin.write(' }');
child.stdin.write(' printf("}");');
child.stdin.write(' }');
child.stdin.write(' }');
child.stdin.write(' printf("]");');
child.stdin.write("}'\nexit\n");
child.waitExit();
try
{
values.linux.usb = JSON.parse(child.stdout.str);
}
catch(x)
{ }
child = null;
}
var pcidevices = require('lib-finder').findBinary('lspci');
if (pcidevices != null)
{
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stderr.str = ''; child.stderr.on('data', dataHandler);
child.stdin.write(pcidevices + " -m | tr '\\n' '`' | ");
child.stdin.write(" awk '");
child.stdin.write('{');
child.stdin.write(' printf("[");');
child.stdin.write(' comma="";');
child.stdin.write(' alen=split($0, lines, "`");');
child.stdin.write(' for(a=1;a<alen;++a)');
child.stdin.write(' {');
child.stdin.write(' match(lines[a], / /);');
child.stdin.write(' blen=split(lines[a], meta, "\\"");');
child.stdin.write(' bus=substr(lines[a], 1, RSTART);');
child.stdin.write(' gsub(/ /, "", bus);');
child.stdin.write(' printf("%s{\\"bus\\": \\"%s\\"", comma, bus); comma=",";');
child.stdin.write(' printf(", \\"device\\": \\"%s\\"", meta[2]);');
child.stdin.write(' printf(", \\"manufacturer\\": \\"%s\\"", meta[4]);');
child.stdin.write(' printf(", \\"description\\": \\"%s\\"", meta[6]);');
child.stdin.write(' if(meta[8] != "")');
child.stdin.write(' {');
child.stdin.write(' printf(", \\"subsystem\\": {");');
child.stdin.write(' printf("\\"manufacturer\\": \\"%s\\"", meta[8]);');
child.stdin.write(' printf(", \\"description\\": \\"%s\\"", meta[10]);');
child.stdin.write(' printf("}");');
child.stdin.write(' }');
child.stdin.write(' printf("}");');
child.stdin.write(' }');
child.stdin.write(' printf("]");');
child.stdin.write("}'\nexit\n");
child.waitExit();
try
{
values.linux.pci = JSON.parse(child.stdout.str);
}
catch (x)
{ }
child = null;
}
// Linux Last Boot Up Time
try {
child = require('child_process').execFile('/usr/bin/uptime', ['', '-s']); // must include blank value at begining for some reason?
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function () { });
child.waitExit();
var regex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
if (regex.test(child.stdout.str.trim())) {
values.linux.LastBootUpTime = child.stdout.str.trim();
} else {
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('date -d "@$(( $(date +%s) - $(awk \'{print int($1)}\' /proc/uptime) ))" "+%Y-%m-%d %H:%M:%S"\nexit\n');
child.waitExit();
if (regex.test(child.stdout.str.trim())) {
values.linux.LastBootUpTime = child.stdout.str.trim();
}
}
child = null;
} catch (ex) { }
// Linux TPM
try {
if (require('fs').statSync('/sys/class/tpm/tpm0').isDirectory()){
values.tpm = {
SpecVersion: require('fs').readFileSync('/sys/class/tpm/tpm0/tpm_version_major').toString().trim()
}
}
} catch (ex) { }
return (values);
}
function windows_wmic_results(str)
{
var lines = str.trim().split('\r\n');
var keys = lines[0].split(',');
var i, key, keyval;
var tokens;
var result = [];
console.log('Lines: ' + lines.length, 'Keys: ' + keys.length);
for (i = 1; i < lines.length; ++i)
{
var obj = {};
console.log('i: ' + i);
tokens = lines[i].split(',');
for (key = 0; key < keys.length; ++key)
{
var tmp = Buffer.from(tokens[key], 'binary').toString();
console.log(tokens[key], tmp);
tokens[key] = tmp == null ? '' : tmp;
if (tokens[key].trim())
{
obj[keys[key].trim()] = tokens[key].trim();
}
}
delete obj.Node;
result.push(obj);
}
return (result);
}
function windows_identifiers()
{
var ret = { windows: {} };
var items, item, i;
ret['identifiers'] = {};
var values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Bios", ['ReleaseDate', 'Manufacturer', 'SMBIOSBIOSVersion', 'SerialNumber']);
if(values[0]){
ret['identifiers']['bios_date'] = values[0]['ReleaseDate'];
ret['identifiers']['bios_vendor'] = values[0]['Manufacturer'];
ret['identifiers']['bios_version'] = values[0]['SMBIOSBIOSVersion'];
ret['identifiers']['bios_serial'] = values[0]['SerialNumber'];
}
ret['identifiers']['bios_mode'] = 'Legacy';
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_BaseBoard", ['Product', 'SerialNumber', 'Manufacturer', 'Version']);
if(values[0]){
ret['identifiers']['board_name'] = values[0]['Product'];
ret['identifiers']['board_serial'] = values[0]['SerialNumber'];
ret['identifiers']['board_vendor'] = values[0]['Manufacturer'];
ret['identifiers']['board_version'] = values[0]['Version'];
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_ComputerSystemProduct", ['UUID', 'Name']);
if(values[0]){
ret['identifiers']['product_uuid'] = values[0]['UUID'];
ret['identifiers']['product_name'] = values[0]['Name'];
trimIdentifiers(ret.identifiers);
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_PhysicalMemory");
if(values[0]){
trimResults(values);
ret.windows.memory = values;
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_OperatingSystem");
if(values[0]){
trimResults(values);
ret.windows.osinfo = values[0];
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskPartition");
if(values[0]){
trimResults(values);
ret.windows.partitions = values;
for (var i in values) {
if (values[i].Description=='GPT: System') {
ret['identifiers']['bios_mode'] = 'UEFI';
}
}
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Processor", ['Caption', 'DeviceID', 'Manufacturer', 'MaxClockSpeed', 'Name', 'SocketDesignation']);
if(values[0]){
ret.windows.cpu = values;
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_VideoController", ['Name', 'CurrentHorizontalResolution', 'CurrentVerticalResolution']);
if(values[0]){
ret.windows.gpu = values;
}
values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskDrive", ['Caption', 'DeviceID', 'Model', 'Partitions', 'Size', 'Status']);
if(values[0]){
ret.windows.drives = values;
}
// Insert GPU names
ret.identifiers.gpu_name = [];
for (var gpuinfo in ret.windows.gpu)
{
if (ret.windows.gpu[gpuinfo].Name) { ret.identifiers.gpu_name.push(ret.windows.gpu[gpuinfo].Name); }
}
// Insert Storage Devices
ret.identifiers.storage_devices = [];
for (var dv in ret.windows.drives)
{
ret.identifiers.storage_devices.push({ Caption: ret.windows.drives[dv].Caption, Model: ret.windows.drives[dv].Model, Size: ret.windows.drives[dv].Size });
}
try { ret.identifiers.cpu_name = ret.windows.cpu[0].Name; } catch (x) { }
// Windows TPM
IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
try {
values = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftTpm', "SELECT * FROM Win32_Tpm", ['IsActivated_InitialValue','IsEnabled_InitialValue','IsOwned_InitialValue','ManufacturerId','ManufacturerVersion','SpecVersion']);
if(values[0]) {
ret.tpm = {
SpecVersion: values[0].SpecVersion.split(",")[0],
ManufacturerId: IntToStr(values[0].ManufacturerId).replace(/[^\x00-\x7F]/g, ""),
ManufacturerVersion: values[0].ManufacturerVersion,
IsActivated: values[0].IsActivated_InitialValue,
IsEnabled: values[0].IsEnabled_InitialValue,
IsOwned: values[0].IsOwned_InitialValue,
}
}
} catch (ex) { }
return (ret);
}
function macos_identifiers()
{
var ret = { identifiers: {}, darwin: {} };
var child;
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep board-id | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
child.waitExit();
ret.identifiers.board_name = child.stdout.str.trim();
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep IOPlatformSerialNumber | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
child.waitExit();
ret.identifiers.board_serial = child.stdout.str.trim();
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep manufacturer | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
child.waitExit();
ret.identifiers.board_vendor = child.stdout.str.trim();
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep version | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
child.waitExit();
ret.identifiers.board_version = child.stdout.str.trim();
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
child.waitExit();
ret.identifiers.product_uuid = child.stdout.str.trim();
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('sysctl -n machdep.cpu.brand_string\nexit\n');
child.waitExit();
ret.identifiers.cpu_name = child.stdout.str.trim();
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('system_profiler SPMemoryDataType\nexit\n');
child.waitExit();
var lines = child.stdout.str.trim().split('\n');
if(lines.length > 0) {
const memorySlots = [];
if(lines[2].trim().includes('Memory Slots:')) { // OLD MACS WITH SLOTS
var memorySlots1 = child.stdout.str.split(/\n{2,}/).slice(3);
memorySlots1.forEach(function(slot,index) {
var lines = slot.split('\n');
if(lines.length == 1){ // start here
if(lines[0].trim()!=''){
var slotObj = { DeviceLocator: lines[0].trim().replace(/:$/, '') }; // Initialize name as an empty string
var nextline = memorySlots1[index+1].split('\n');
nextline.forEach(function(line) {
if (line.trim() !== '') {
var parts = line.split(':');
var key = parts[0].trim();
var value = parts[1].trim();
value = (key == 'Part Number' || key == 'Manufacturer') ? hexToAscii(parts[1].trim()) : parts[1].trim();
slotObj[key.replace(' ','')] = value; // Store attribute in the slot object
}
});
memorySlots.push(slotObj);
}
}
});
} else { // NEW MACS WITHOUT SLOTS
memorySlots.push({ DeviceLocator: "Onboard Memory", Size: lines[2].split(":")[1].trim(), PartNumber: lines[3].split(":")[1].trim(), Manufacturer: lines[4].split(":")[1].trim() })
}
ret.darwin.memory = memorySlots;
}
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('diskutil info -all\nexit\n');
child.waitExit();
var sections = child.stdout.str.split('**********\n');
if(sections.length > 0){
var devices = [];
for (var i = 0; i < sections.length; i++) {
var lines = sections[i].split('\n');
var deviceInfo = {};
var wholeYes = false;
var physicalYes = false;
var oldmac = false;
for (var j = 0; j < lines.length; j++) {
var keyValue = lines[j].split(':');
var key = keyValue[0].trim();
var value = keyValue[1] ? keyValue[1].trim() : '';
if (key === 'Virtual') oldmac = true;
if (key === 'Whole' && value === 'Yes') wholeYes = true;
if (key === 'Virtual' && value === 'No') physicalYes = true;
if(value && key === 'Device / Media Name'){
deviceInfo['Caption'] = value;
}
if(value && key === 'Disk Size'){
deviceInfo['Size'] = value.split(' ')[0] + ' ' + value.split(' ')[1];
}
}
if (wholeYes) {
if (oldmac) {
if (physicalYes) devices.push(deviceInfo);
} else {
devices.push(deviceInfo);
}
}
}
ret.identifiers.storage_devices = devices;
}
// Fetch storage volumes using df
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write('df -aHY | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\",\\"type\\":\\"%s\\"},", $3, $4, $5, $10, $2}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
child.waitExit();
try {
ret.darwin.volumes = JSON.parse(child.stdout.str.trim());
for (var index = 0; index < ret.darwin.volumes.length; index++) {
if (ret.darwin.volumes[index].type == 'auto_home'){
ret.darwin.volumes.splice(index,1);
}
}
if (ret.darwin.volumes.length == 0) { // not sonima OS so dont show type for now
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', dataHandler);
child.stdin.write('df -aH | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\"},", $2, $3, $4, $9}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
child.waitExit();
try {
ret.darwin.volumes = JSON.parse(child.stdout.str.trim());
for (var index = 0; index < ret.darwin.volumes.length; index++) {
if (ret.darwin.volumes[index].size == 'auto_home'){
ret.darwin.volumes.splice(index,1);
}
}
} catch (xx) { }
}
} catch (xx) { }
child = null;
// MacOS Last Boot Up Time
try {
child = require('child_process').execFile('/usr/sbin/sysctl', ['', 'kern.boottime']); // must include blank value at begining for some reason?
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function () { });
child.waitExit();
const timestampMatch = /\{ sec = (\d+), usec = \d+ \}/.exec(child.stdout.str.trim());
if (!ret.darwin) {
ret.darwin = { LastBootUpTime: parseInt(timestampMatch[1]) };
} else {
ret.darwin.LastBootUpTime = parseInt(timestampMatch[1]);
}
child = null;
} catch (ex) { }
trimIdentifiers(ret.identifiers);
child = null;
return (ret);
}
function hexToAscii(hexString) {
if(!hexString.startsWith('0x')) return hexString.trim();
hexString = hexString.startsWith('0x') ? hexString.slice(2) : hexString;
var str = '';
for (var i = 0; i < hexString.length; i += 2) {
var hexPair = hexString.substr(i, 2);
str += String.fromCharCode(parseInt(hexPair, 16));
}
str = str.replace(/[\u007F-\uFFFF]/g, ''); // Remove characters from 0x0080 to 0xFFFF
return str.trim();
}
function win_chassisType()
{
// needs to be replaced with win-wmi but due to bug in win-wmi it doesnt handle arrays correctly
var child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], {});
if (child == null) { return ([]); }
child.descriptorMetadata = 'process-manager';
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('Get-WmiObject Win32_SystemEnclosure | Select-Object -ExpandProperty ChassisTypes\r\n');
child.stdin.write('exit\r\n');
child.waitExit();
try {
return (parseInt(child.stdout.str));
} catch (e) {
return (2); // unknown
}
}
function win_systemType()
{
try {
var tokens = require('win-wmi').query('ROOT\\CIMV2', 'SELECT PCSystemType FROM Win32_ComputerSystem', ['PCSystemType']);
if (tokens[0]) {
return (parseInt(tokens[0]['PCSystemType']));
} else {
return (parseInt(1)); // default is desktop
}
} catch (ex) {
return (parseInt(1)); // default is desktop
}
}
function win_formFactor(chassistype)
{
var ret = 'DESKTOP';
switch (chassistype)
{
case 11: // Handheld
case 30: // Tablet
case 31: // Convertible
case 32: // Detachable
ret = 'TABLET';
break;
case 9: // Laptop
case 10: // Notebook
case 14: // Sub Notebook
ret = 'LAPTOP';
break;
default:
ret = win_systemType() == 2 ? 'MOBILE' : 'DESKTOP';
break;
}
return (ret);
}
switch(process.platform)
{
case 'linux':
module.exports = { _ObjectID: 'identifiers', get: linux_identifiers };
break;
case 'win32':
module.exports = { _ObjectID: 'identifiers', get: windows_identifiers, chassisType: win_chassisType, formFactor: win_formFactor, systemType: win_systemType };
break;
case 'darwin':
module.exports = { _ObjectID: 'identifiers', get: macos_identifiers };
break;
default:
module.exports = { get: function () { throw ('Unsupported Platform'); } };
break;
}
module.exports.isDocker = function isDocker()
{
if (process.platform != 'linux') { return (false); }
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stdin.write("cat /proc/self/cgroup | tr '\n' '`' | awk -F'`' '{ split($1, res, " + '"/"); if(res[2]=="docker"){print "1";} }\'\nexit\n');
child.waitExit();
return (child.stdout.str != '');
};
module.exports.isBatteryPowered = function isBatteryOperated()
{
var ret = false;
switch(process.platform)
{
default:
break;
case 'linux':
var devices = require('fs').readdirSync('/sys/class/power_supply');
for (var i in devices)
{
if (require('fs').readFileSync('/sys/class/power_supply/' + devices[i] + '/type').toString().trim() == 'Battery')
{
ret = true;
break;
}
}
break;
case 'win32':
var GM = require('_GenericMarshal');
var stats = GM.CreateVariable(12);
var kernel32 = GM.CreateNativeProxy('Kernel32.dll');
kernel32.CreateMethod('GetSystemPowerStatus');
if (kernel32.GetSystemPowerStatus(stats).Val != 0)
{
if(stats.toBuffer()[1] != 128 && stats.toBuffer()[1] != 255)
{
ret = true;
}
else
{
// No Battery detected, so lets check if there is supposed to be one
var formFactor = win_formFactor(win_chassisType());
return (formFactor == 'LAPTOP' || formFactor == 'TABLET' || formFactor == 'MOBILE');
}
}
break;
case 'darwin':
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("pmset -g batt | tr '\\n' '`' | awk -F'`' '{ if(NF>2) { print \"true\"; }}'\nexit\n");
child.waitExit();
if(child.stdout.str.trim() != '') { ret = true; }
break;
}
return (ret);
};
module.exports.isVM = function isVM()
{
var ret = false;
var id = this.get();
if (id.linux && id.linux.sys_vendor)
{
switch (id.linux.sys_vendor)
{
case 'VMware, Inc.':
case 'QEMU':
case 'Xen':
ret = true;
break;
default:
break;
}
}
if (id.identifiers.bios_vendor)
{
switch(id.identifiers.bios_vendor)
{
case 'VMware, Inc.':
case 'Xen':
case 'SeaBIOS':
case 'EFI Development Kit II / OVMF':
case 'Proxmox distribution of EDK II':
ret = true;
break;
default:
break;
}
}
if (id.identifiers.board_vendor && id.identifiers.board_vendor == 'VMware, Inc.') { ret = true; }
if (id.identifiers.board_name)
{
switch (id.identifiers.board_name)
{
case 'VirtualBox':
case 'Virtual Machine':
ret = true;
break;
default:
break;
}
}
if (process.platform == 'win32' && !ret)
{
for(var i in id.identifiers.gpu_name)
{
if(id.identifiers.gpu_name[i].startsWith('VMware '))
{
ret = true;
break;
}
}
}
if (!ret) { ret = this.isDocker(); }
return (ret);
};
// bios_date = BIOS->ReleaseDate
// bios_vendor = BIOS->Manufacturer
// bios_version = BIOS->SMBIOSBIOSVersion
// board_name = BASEBOARD->Product = ioreg/board-id
// board_serial = BASEBOARD->SerialNumber = ioreg/serial-number | ioreg/IOPlatformSerialNumber
// board_vendor = BASEBOARD->Manufacturer = ioreg/manufacturer
// board_version = BASEBOARD->Version

View file

@ -110,14 +110,10 @@
"ko": {
"allow": "허용하다",
"deny": "거부",
"terminalNotify": "{0}이(가) 원격 터미널 세션을 시작했습니다.",
"desktopNotify": "{0}이(가) 원격 데스크톱 세션을 시작했습니다.",
"fileNotify": "{0}이(가) 원격 파일 세션을 시작했습니다.",
"privacyBar": "다음과 데스크톱 공유: {0}",
"autoAllowForFive": "다음 5분 동안 모든 연결 자동 수락",
"terminalConsent": "{0} 원격 터미널 액세스를 요청합니다. 액세스 권한을 부여하시겠습니까?",
"desktopConsent": "{0} 원격 데스크톱 액세스를 요청합니다. 액세스 권한을 부여하시겠습니까?",
"fileConsent": "{0}이(가) 원격 파일 액세스를 요청합니다. 액세스 권한을 부여하시겠습니까?"
"terminalNotify": "원격 터미널 세션이 시작되었습니다: {0}",
"desktopNotify": "원격 데스크톱 세션이 시작되었습니다: {0}",
"fileNotify": "원격 파일 전송 세션이 시작되었습니다: {0}",
"privacyBar": "다음과 데스크탑 공유: {0}"
},
"nl": {
"allow": "Toestaan",
@ -133,15 +129,7 @@
},
"pt": {
"allow": "Permitir",
"deny": "Negar",
"autoAllowForFive": "Aceita automaticamente todas as conexões pelos próximos 5 minutos",
"terminalConsent": "{0} está a pedir acesso ao terminal remoto. Conceder acesso?",
"desktopConsent": "{0} está a pedir acesso à área de trabalho remota. Conceder acesso?",
"fileConsent": "{0} está a pedir acesso remoto aos ficheiros. Conceder acesso?",
"terminalNotify": "{0} iniciou uma sessão de terminal remoto.",
"desktopNotify": "{0} iniciou uma sessão de área de trabalho remota.",
"fileNotify": "{0} iniciou uma sessão de ficheiro remoto.",
"privacyBar": "Compartilhando área de trabalho com: {0}"
"deny": "Negar"
},
"ru": {
"allow": "Разрешить",
@ -157,15 +145,7 @@
},
"sv": {
"allow": "Tillåta",
"deny": "Förneka",
"autoAllowForFive": "Acceptera alla anslutningar automatiskt under de kommande 5 minuterna",
"terminalConsent": "{0} begär åtkomst till fjärrterminal. Ge tillgång?",
"desktopConsent": "{0} begär åtkomst till fjärrskrivbord. Ge tillgång?",
"fileConsent": "{0} begär fjärråtkomst till fil. Ge tillgång?",
"terminalNotify": "{0} startade en fjärrterminalsession.",
"desktopNotify": "{0} startade en fjärrskrivbordssession.",
"fileNotify": "{0} startade en fjärrfilsession.",
"privacyBar": "Dela skrivbord med: {0}"
"deny": "Förneka"
},
"tr": {
"allow": "İzin ver",
@ -181,15 +161,7 @@
},
"zh-chs": {
"allow": "允许",
"deny": "否定",
"autoAllowForFive": "在接下来的 5 分钟内自动接受所有连接",
"terminalConsent": "{0} 请求远程终端访问。授予访问权限?",
"desktopConsent": "{0} 请求远程桌面访问。授予访问权限?",
"fileConsent": "{0} 请求远程文件访问。授予访问权限?",
"terminalNotify": "{0} 启动了远程终端会话。",
"desktopNotify": "{0} 启动了远程桌面会话。",
"fileNotify": "{0} 启动了远程文件会话。",
"privacyBar": "与:{0} 共享桌面"
"deny": "否定"
},
"da": {
"allow": "Tillad",
@ -226,65 +198,5 @@
"desktopNotify": "{0} iniciou uma sessão de área de trabalho remota.",
"fileNotify": "{0} {0} iniciou uma sessão de arquivos.",
"privacyBar": "Compartilhando área de trabalho com: {0}"
},
"zh-cht": {
"allow": "允許",
"deny": "否定",
"autoAllowForFive": "在接下來的 5 分鐘內自動接受所有連接",
"terminalConsent": "{0} 請求遠程終端訪問。授予訪問權限?",
"desktopConsent": "{0} 請求遠程桌面訪問。授予訪問權限?",
"fileConsent": "{0} 請求遠程文件訪問。授予訪問權限?",
"terminalNotify": "{0} 啟動了遠程終端會話。",
"desktopNotify": "{0} 啟動了遠程桌面會話。",
"fileNotify": "{0} 啟動了遠程文件會話。",
"privacyBar": "與:{0} 共享桌面"
},
"bs": {
"allow": "Dopustiti",
"deny": "Deny",
"autoAllowForFive": "Automatski prihvati sve veze u narednih 5 minuta",
"terminalConsent": "{0} zahtijeva pristup udaljenom terminalu. Odobriti pristup?",
"desktopConsent": "{0} zahtijeva pristup udaljenoj radnoj površini. Odobriti pristup?",
"fileConsent": "{0} zahtijeva udaljeni pristup fajlu. Odobriti pristup?",
"terminalNotify": "{0} je započeo sesiju udaljenog terminala.",
"desktopNotify": "{0} je započeo sesiju udaljene radne površine.",
"fileNotify": "{0} je započeo sesiju udaljenog fajla.",
"privacyBar": "Dijeljenje radne površine sa: {0}"
},
"hu": {
"allow": "Engedélyezés",
"deny": "Elutasítás",
"autoAllowForFive": "Csatlakozások automatikus elfogadása a következő 5 percben",
"terminalConsent": "{0} távoli parancssor,terminál hozzáférést kér. Engedélyezi a hozzáférést?",
"desktopConsent": "{0} távoli asztali hozzáférést kér. Engedélyezi a hozzáférést?",
"fileConsent": "{0} távoli fájlhozzáférést kér. Engedélyezi a hozzáférést?",
"terminalNotify": "{0} távoli parancssor munkamenetet indított.",
"desktopNotify": "{0} távoli asztali munkamenetet indított.",
"fileNotify": "{0} távoli fájlmunkamenetet indított.",
"privacyBar": "Asztal megosztás aktív: {0} felhasználóval"
},
"ca": {
"allow": "Permetre",
"deny": "Negar",
"autoAllowForFive": "Accepta automàticament totes les connexions durant els propers 5 minuts",
"terminalConsent": "{0} sol·licitant accés al terminal remot. Accés garantit?",
"desktopConsent": "{0} sol·licitant accés a l'escriptori remot. Accés garantit?",
"fileConsent": "{0} sol·licitant accés remot al fitxer. Accés garantit?",
"terminalNotify": "{0} va iniciar una sessió de terminal remota.",
"desktopNotify": "{0} va iniciar una sessió d'escriptori remot.",
"fileNotify": "{0} va iniciar una sessió de fitxer remota.",
"privacyBar": "Compartint escriptori amb: {0}"
},
"uk": {
"allow": "Дозволити",
"deny": "Відмовити",
"autoAllowForFive": "Автоматично приймати всі підключення впродовж наступних 5 хвилин",
"terminalConsent": "{0} запитує доступ до віддаленого терміналу. Надати доступ?",
"desktopConsent": "{0} запитує віддалений доступ до стільниці. Надати доступ?",
"fileConsent": "{0} запитує віддалений доступ до файлу. Надати доступ?",
"terminalNotify": "{0} почав сеанс віддаленого терміналу.",
"desktopNotify": "{0} розпочав сеанс віддаленої стільниці.",
"fileNotify": "{0} розпочав віддалений файловий сеанс.",
"privacyBar": "Поширити доступ до стільниці з: {0}"
}
}

View file

@ -269,18 +269,15 @@ function SMBiosTables()
this.amtInfo = function amtInfo(data) {
if (!data) { throw ('no data'); }
var retVal = { AMT: false };
if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT')
{
if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') {
var amt = data[130].peek();
retVal.AMT = amt[4] ? true : false;
if (retVal.AMT)
{
if (retVal.AMT) {
retVal.enabled = amt[5] ? true : false;
retVal.storageRedirection = amt[6] ? true : false;
retVal.serialOverLan = amt[7] ? true : false;
retVal.kvm = amt[14] ? true : false;
if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro')
{
if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') {
var settings = data[131].peek();
if (settings[0] & 0x04) { retVal.TXT = (settings[0] & 0x08) ? true : false; }
if (settings[0] & 0x10) { retVal.VMX = (settings[0] & 0x20) ? true : false; }
@ -298,14 +295,6 @@ function SMBiosTables()
}
}
}
if (!retVal.AMT)
{
if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro')
{
var settings = data[131].peek();
if ((settings[20] & 0x08) == 0x08) { retVal.AMT = true; }
}
}
return (retVal);
};
this.smTableTypes = {

View file

@ -159,7 +159,7 @@ function linux_memUtilization()
case 'MemTotal:':
ret.total = parseInt(tokens[tokens.length - 2]);
break;
case 'MemAvailable:':
case 'MemFree:':
ret.free = parseInt(tokens[tokens.length - 2]);
break;
}
@ -209,13 +209,9 @@ function macos_memUtilization()
{
var usage = lines[0].split(':')[1];
var bdown = usage.split(',');
if (bdown.length > 2){ // new style - PhysMem: 5750M used (1130M wired, 634M compressor), 1918M unused.
mem.MemFree = parseInt(bdown[2].trim().split(' ')[0]);
} else { // old style - PhysMem: 6683M used (1606M wired), 9699M unused.
mem.MemFree = parseInt(bdown[1].trim().split(' ')[0]);
}
mem.MemUsed = parseInt(bdown[0].trim().split(' ')[0]);
mem.MemTotal = (mem.MemFree + mem.MemUsed);
mem.MemTotal = parseInt(bdown[0].trim().split(' ')[0]);
mem.MemFree = parseInt(bdown[1].trim().split(' ')[0]);
mem.percentFree = ((mem.MemFree / mem.MemTotal) * 100);//.toFixed(2);
mem.percentConsumed = (((mem.MemTotal - mem.MemFree) / mem.MemTotal) * 100);//.toFixed(2);
return (mem);
@ -229,48 +225,31 @@ function macos_memUtilization()
function windows_thermals()
{
var ret = [];
try {
ret = require('win-wmi').query('ROOT\\WMI', 'SELECT CurrentTemperature,InstanceName FROM MSAcpi_ThermalZoneTemperature',['CurrentTemperature','InstanceName']);
if (ret[0]) {
for (var i = 0; i < ret.length; ++i) {
ret[i]['CurrentTemperature'] = ((parseFloat(ret[i]['CurrentTemperature']) / 10) - 273.15).toFixed(2);
}
child = require('child_process').execFile(process.env['windir'] + '\\System32\\wbem\\wmic.exe', ['wmic', '/namespace:\\\\root\\wmi', 'PATH', 'MSAcpi_ThermalZoneTemperature', 'get', 'CurrentTemperature']);
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.waitExit();
if(child.stdout.str.trim!='')
{
var lines = child.stdout.str.trim().split('\r\n');
for (var i = 1; i < lines.length; ++i)
{
if (lines[i].trim() != '') { ret.push(((parseFloat(lines[i]) / 10) - 273.15).toFixed(2)); }
}
} catch (ex) { }
}
return (ret);
}
function linux_thermals()
{
var ret = [];
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");
child.stdin.write("cat /sys/class/thermal/thermal_zone*/temp | awk '{ print $0 / 1000 }'\nexit\n");
child.waitExit();
if(child.stdout.str.trim()!='')
{
var lines = child.stdout.str.trim().split('\n');
for (var i = 0; i < lines.length; ++i)
{
var line = lines[i].trim().split(',');
ret.push({CurrentTemperature: (parseFloat(line[0])/1000), InstanceName: line[1]});
}
}
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 mon in /sys/class/hwmon/hwmon*; do for label in \"$mon\"/temp*_label; do if [ -f $label ]; then echo $(cat \"$label\")___$(cat \"${label%_*}_input\"); fi; done; done;\nexit\n");
child.waitExit();
if(child.stdout.str.trim()!='')
{
var lines = child.stdout.str.trim().split('\n');
for (var i = 0; i < lines.length; ++i)
{
var line = lines[i].trim().split('___');
ret.push({ CurrentTemperature: (parseFloat(line[1])/1000), InstanceName: line[0] });
}
}
var ret = child.stdout.str.trim().split('\n');
if (ret.length == 1 && ret[0] == '') { ret = []; }
return (ret);
}
@ -282,6 +261,7 @@ function macos_thermals()
child.stderr.on('data', function () { });
child.stdin.write('powermetrics --help | grep SMC\nexit\n');
child.waitExit();
if (child.stdout.str.trim() != '')
{
child = require('child_process').execFile('/bin/sh', ['sh']);
@ -293,19 +273,14 @@ function macos_thermals()
{
if (tokens[i].split(' die temperature: ').length > 1)
{
ret.push({CurrentTemperature: tokens[i].split(' ')[3], InstanceName: tokens[i].split(' ')[0]});
ret.push(tokens[i].split(' ')[3]);
this.parent.kill();
}
}
});
child.stderr.on('data', function (c) {
if (c.toString().split('unable to get smc values').length > 1) { // error getting sensors so just kill
this.parent.kill();
return;
}
});
child.stdin.write('powermetrics -s smc -i 500 -n 1\n');
child.waitExit(2000);
child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
child.stdin.write('powermetrics -s smc\n');
child.waitExit(5000);
}
return (ret);
}

View file

@ -1,194 +0,0 @@
/*
Copyright 2022 Intel Corporation
@author Bryan Roe
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.
*/
//
// win-deskutils is a utility module that exposes various desktop related features for Windows
// such as MouseTrails Accessability and Windows Desktop Background
//
//
// MSDN documention for the system call this module relies on can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
//
var SPI_GETDESKWALLPAPER = 0x0073;
var SPI_SETDESKWALLPAPER = 0x0014;
var SPI_GETMOUSETRAILS = 0x005E;
var SPI_SETMOUSETRAILS = 0x005D;
var GM = require('_GenericMarshal');
var user32 = GM.CreateNativeProxy('user32.dll');
user32.CreateMethod('SystemParametersInfoA');
//
// This function is a helper method to dispatch method calls to different user sessions
//
function sessionDispatch(tsid, parent, method, args)
{
//
// Check to see if the process owner of the current processor is root
//
var sid = undefined;
var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
/*
The following is the list of possible values for stype.
If the current process owner is root, we set the stype to user,
because we cannot set/get any properties from this user, we
must switch to a user session.. Default behavior for stype(1)
is that it will context switch to the logged in user. If
this is not intended, then an actual user TSID must be specified, using
ILibProcessPipe_SpawnTypes_SPECIFIED_USER and the actual TSID
------------------------------------------------------------------------
ILibProcessPipe_SpawnTypes_DEFAULT = 0,
ILibProcessPipe_SpawnTypes_USER = 1,
ILibProcessPipe_SpawnTypes_WINLOGON = 2,
ILibProcessPipe_SpawnTypes_TERM = 3,
ILibProcessPipe_SpawnTypes_DETACHED = 4,
ILibProcessPipe_SpawnTypes_SPECIFIED_USER = 5,
ILibProcessPipe_SpawnTypes_POSIX_DETACHED = 0x8000
------------------------------------------------------------------------
*/
console.log('stype: ' + stype);
if (stype == 1)
{
if (tsid == null && require('MeshAgent')._tsid != null)
{
stype = 5; // ILibProcessPipe_SpawnTypes_SPECIFIED_USER
sid = require('MeshAgent')._tsid; // If this is set, it was set via user selection UI
}
else
{
sid = tsid; // Set the SID to be whatever was passed in
}
}
// Spawn a child process in the appropriate user session, and relay the response back via stdout
var mod = Buffer.from(getJSModule('win-deskutils')).toString('base64');
var prog = "try { addModule('win-deskutils', process.env['win_deskutils']);} catch (x) { } var x;try{x=require('win-deskutils').dispatch('" + parent + "', '" + method + "', " + JSON.stringify(args) + ");console.log(x);}catch(z){console.log(z);process.exit(1);}process.exit(0);";
var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', Buffer.from(prog).toString('base64')], { type: stype, uid: sid, env: { win_deskutils: getJSModule('win-deskutils') } });
child.stdout.str = '';
child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function (c) { });
child.on('exit', function (c) { this.exitCode = c; });
child.waitExit();
if (child.exitCode == 0)
{
return (child.stdout.str.trim()); // If the return code was 0, then relay the response from stdout
}
else
{
throw (child.stdout.str.trim()); // If the return code was nonzero, then the stdout response is the exception that should be bubbled
}
}
//
// This function gets the path of the windows desktop background of the specified user desktop session
//
function background_get(tsid)
{
if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
{
// Need to disatch to different session first
return (sessionDispatch(tsid, 'background', 'get', []));
}
var v = GM.CreateVariable(1024);
var ret = user32.SystemParametersInfoA(SPI_GETDESKWALLPAPER, v._size, v, 0);
if (ret.Val == 0)
{
throw ('Error occured trying to fetch wallpaper');
}
return (v.String);
}
//
// This function sets the path for the windows desktop background of the specified user desktop session
//
function background_set(path, tsid)
{
if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
{
// Need to disatch to different session first
return (sessionDispatch(tsid, 'background', 'set', [path]));
}
var nb = GM.CreateVariable(path);
var ret = user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, nb._size, nb, 0);
if (ret.Val == 0)
{
throw ('Error occured trying to set wallpaper');
}
return;
}
//
// This is a helper function that is called by the child process from sessionDispatch()
//
function dispatch(parent, method, args)
{
try
{
return (this[parent][method].apply(this, args));
}
catch (e)
{
console.log('ERROR: ' + e);
throw ('Error occured trying to dispatch: ' + method);
}
}
//
// This function sets the mousetrail accessibility feature, for the specified user desktop session.
// Setting value 0 or one disables this feature
// Otherwise, value is the number of cursors to render for this feature
//
function mousetrails_set(value, tsid)
{
if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
{
// Need to disatch to different session first
return (sessionDispatch(tsid, 'mouse', 'setTrails', [value]));
}
var ret = user32.SystemParametersInfoA(SPI_SETMOUSETRAILS, value, 0, 0);
if (ret.Val == 0)
{
throw ('Error occured trying to fetch wallpaper');
}
}
//
// This function returns the number of cursors the mousetrail accessibility feature will render
// A value of 0 or 1 means the feature is disabled, otherwise it is the number of cursors that will be rendered
//
function mousetrails_get(tsid)
{
if (tsid != null || tsid === null) // TSID is not undefined or is explicitly null
{
// Need to disatch to different session first
return (sessionDispatch(tsid, 'mouse', 'getTrails', []));
}
var v = GM.CreateVariable(4);
var ret = user32.SystemParametersInfoA(SPI_GETMOUSETRAILS, v._size, v, 0);
if (ret.Val == 0)
{
throw ('Error occured trying to fetch wallpaper');
}
return (v.toBuffer().readUInt32LE());
}
module.exports = { background: { get: background_get, set: background_set } };
module.exports.mouse = { getTrails: mousetrails_get, setTrails: mousetrails_set };
module.exports.dispatch = dispatch;

View file

@ -18,21 +18,28 @@ var promise = require('promise');
function qfe()
{
try {
var tokens = require('win-wmi').query('ROOT\\CIMV2', 'SELECT * FROM Win32_QuickFixEngineering');
if (tokens[0]){
for (var index = 0; index < tokens.length; index++) {
for (var key in tokens[index]) {
if (key.startsWith('__')) delete tokens[index][key];
}
}
return (tokens);
} else {
return ([]);
var child = require('child_process').execFile(process.env['windir'] + '\\System32\\wbem\\wmic.exe', ['wmic', 'qfe', 'list', 'full', '/FORMAT:CSV']);
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.waitExit();
var lines = child.stdout.str.trim().split('\r\n');
var keys = lines[0].split(',');
var i, key;
var tokens;
var result = [];
for (i = 1; i < lines.length; ++i)
{
var obj = {};
tokens = lines[i].split(',');
for (key = 0; key < keys.length; ++key)
{
if (tokens[key]) { obj[keys[key]] = tokens[key]; }
}
} catch (ex) {
return ([]);
result.push(obj);
}
return (result);
}
function av()
{
@ -46,23 +53,9 @@ function av()
child.stdin.write('[reflection.Assembly]::LoadWithPartialName("system.core")\r\n');
child.stdin.write('Get-WmiObject -Namespace "root/SecurityCenter2" -Class AntiVirusProduct | ');
child.stdin.write('ForEach-Object -Process { ');
child.stdin.write('$matches = [regex]::Matches($_.pathToSignedProductExe, "%(.*?)%"); ');
child.stdin.write('$modifiedPath = $_.pathToSignedProductExe; ');
child.stdin.write('foreach ($match in $matches) { ');
child.stdin.write('$modifiedPath = $modifiedPath -replace [regex]::Escape($match.Value), [System.Environment]::GetEnvironmentVariable($match.Groups[1].Value, "Process") ');
child.stdin.write('} ');
child.stdin.write('$flag = $true; ');
child.stdin.write('if ($modifiedPath -ne "windowsdefender://"){ ');
child.stdin.write('if (-not (Test-Path -Path $modifiedPath -PathType Leaf)) { ');
child.stdin.write('$flag = $false; ');
child.stdin.write('} ');
child.stdin.write('} ');
child.stdin.write('if ($flag -eq $true) { ')
child.stdin.write('$Bytes = [System.Text.Encoding]::UTF8.GetBytes($_.displayName); ');
child.stdin.write('$EncodedText =[Convert]::ToBase64String($Bytes); ');
child.stdin.write('Write-Output ("{0},{1}" -f $_.productState,$EncodedText); ');
child.stdin.write('} ');
child.stdin.write('}\r\n ');
child.stdin.write('Write-Host ("{0},{1}" -f $_.productState,$EncodedText); }\r\n');
child.stdin.write('exit\r\n');
child.waitExit();
@ -221,14 +214,6 @@ function installedApps()
catch(e)\
{\
}\
try\
{\
val.installdate = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\' + items.subkeys[key], 'InstallDate');\
if (val.installdate == '') { delete val.installdate; }\
}\
catch(e)\
{\
}\
result.push(val);\
}\
console.log(JSON.stringify(result,'', 1));process.exit();";
@ -240,33 +225,12 @@ function installedApps()
return (ret);
}
function defender(){
var promise = require('promise');
var ret = new promise(function (a, r) { this._resolve = a; this._reject = r; });
ret.child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], {});
ret.child.promise = ret;
ret.child.stdout.str = ''; ret.child.stdout.on('data', function (c) { this.str += c.toString(); });
ret.child.stderr.str = ''; ret.child.stderr.on('data', function (c) { this.str += c.toString(); });
ret.child.stdin.write('Get-MpComputerStatus | Select-Object RealTimeProtectionEnabled,IsTamperProtected | ConvertTo-JSON\r\n');
ret.child.stdin.write('exit\r\n');
ret.child.on('exit', function (c) {
if (this.stdout.str == '') { this.promise._resolve({}); return; }
try {
var abc = JSON.parse(this.stdout.str.trim());
this.promise._resolve({ RealTimeProtection: abc.RealTimeProtectionEnabled, TamperProtected: abc.IsTamperProtected });
} catch (ex) {
this.promise._resolve({}); return;
}
});
return (ret);
}
if (process.platform == 'win32')
{
module.exports = { qfe: qfe, av: av, defrag: defrag, pendingReboot: pendingReboot, installedApps: installedApps, defender: defender };
module.exports = { qfe: qfe, av: av, defrag: defrag, pendingReboot: pendingReboot, installedApps: installedApps };
}
else
{
var not_supported = function () { throw (process.platform + ' not supported'); };
module.exports = { qfe: not_supported, av: not_supported, defrag: not_supported, pendingReboot: not_supported, installedApps: not_supported, defender: not_supported };
module.exports = { qfe: not_supported, av: not_supported, defrag: not_supported, pendingReboot: not_supported, installedApps: not_supported };
}

View file

@ -1,5 +1,5 @@
/*
Copyright 2018-2022 Intel Corporation
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -89,50 +89,21 @@ function windows_terminal() {
var newCsbi = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; }
if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY)
{
//
// Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
// https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
//
if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY) {
//wchar_t mywbuf[512];
//swprintf(mywbuf, 512, TEXT("csbi.dwCursorPosition.X = %d, csbi.dwCursorPosition.Y = %d, newCsbi.dwCursorPosition.X = %d, newCsbi.dwCursorPosition.Y = %d\r\n"), csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, newCsbi.dwCursorPosition.X, newCsbi.dwCursorPosition.Y);
//OutputDebugString(mywbuf);
//m_viewOffset = newCsbi.srWindow.Top;
//WriteMoveCursor((SerialAgent *)this->sa, (char)(newCsbi.dwCursorPosition.Y - m_viewOffset), (char)(newCsbi.dwCursorPosition.X - m_viewOffset));
//LowStackSendData((SerialAgent *)(this->sa), "", 0);
this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE();
this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE();
}
}
this.ClearScreen = function ()
{
//
// Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
// https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
//
//
// Reference for GetConsoleScreenBufferInfo can be found at:
// https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
//
//
// Reference for FillConsoleOutputCharacter can be found at:
// https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter
//
//
// Reference for FillConsoleOutputAttribute can be found at:
// https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputattribute
//
//
// Reference for SetConsoleCursorPosition can be found at:
// https://learn.microsoft.com/en-us/windows/console/setconsolecursorposition
//
//
// Reference for SetConsoleWindowInfo can be fount at:
// https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
//
this.ClearScreen = function () {
var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
@ -161,7 +132,6 @@ function windows_terminal() {
this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect);
}
// This does a rudimentary check if the platform is capable of PowerShell
this.PowerShellCapable = function()
{
if (require('os').arch() == 'x64')
@ -174,7 +144,6 @@ function windows_terminal() {
}
}
// Starts a Legacy Windows Terminal Session
this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget)
{
// The older windows terminal does not support
@ -195,9 +164,7 @@ function windows_terminal() {
this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
this._connected = false;
// Coord structure can be found at: https://learn.microsoft.com/en-us/windows/console/coord-str
var coordScreen = GM.CreateVariable(4);
var coordScreen = GM.CreateVariable(4);
coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH);
coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT);
@ -205,21 +172,10 @@ function windows_terminal() {
rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
//
// Reference for SetConsoleWindowInfo can be found at:
// https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
//
if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0)
{
if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0) {
throw ('Failed to set Console Screen Size');
}
//
// Reference for SetConsoleScreenBufferSize can be found at:
// https://learn.microsoft.com/en-us/windows/console/setconsolescreenbuffersize
//
if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0)
{
if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0) {
throw ('Failed to set Console Buffer Size');
}
@ -322,16 +278,8 @@ function windows_terminal() {
return (this.stopping);
}
//
// This function uses the SetWinEventHook() method, so we can hook
// All events between EVENT_CONSOLE_CARET and EVENT_CONSOLE_END_APPLICATION
//
this._hookThread = function ()
{
//
// Reference for SetWinEventHook() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook
//
var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
ret.userArgs = [];
for (var a in arguments)
@ -344,43 +292,24 @@ function windows_terminal() {
var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
p.ready = ret;
p.terminal = this;
p.then(function (hwinEventHook)
{
if (hwinEventHook.Val == 0)
{
p.then(function (hwinEventHook) {
if (hwinEventHook.Val == 0) {
this.ready._rej('Error calling SetWinEventHook');
} else
{
} else {
this.terminal.hwinEventHook = hwinEventHook;
this.ready._res();
this.terminal._GetMessage();
}
});
//
// This is the WINEVENTPROC callback for the WinEventHook we set
//
this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime)
{
//
// Reference for WINEVENTPROC can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wineventproc
//
this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime) {
if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; }
var buffer = null;
//
// Reference for Console WinEvents can be found at:
// https://learn.microsoft.com/en-us/windows/console/console-winevents
//
switch (dwEvent.Val)
{
switch (dwEvent.Val) {
case EVENT_CONSOLE_CARET:
// The console caret has moved
break;
case EVENT_CONSOLE_UPDATE_REGION:
// More than one character has changed
if (!this.terminal.connected) {
this.terminal.connected = true;
this.terminal._stream._promise._res();
@ -392,30 +321,25 @@ function windows_terminal() {
}
break;
case EVENT_CONSOLE_UPDATE_SIMPLE:
// A single character has changed
//console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']');
var simplebuffer = { data: [ Buffer.alloc(1, LOWORD(idChild.Val)) ], attributes: [ HIWORD(idChild.Val) ], width: 1, height: 1, x: LOWORD(idObject.Val), y: HIWORD(idObject.Val) };
this.terminal._SendDataBuffer(simplebuffer);
break;
case EVENT_CONSOLE_UPDATE_SCROLL:
// The console has scrolled
//console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']');
this.terminal._SendScroll(idObject.Val, idChild.Val);
break;
case EVENT_CONSOLE_LAYOUT:
// The console layout has changed.
//console.log('CONSOLE_LAYOUT');
//snprintf( Buf, 512, "Event Console LAYOUT!\r\n");
//SendLayout();
break;
case EVENT_CONSOLE_START_APPLICATION:
// A new console process has started
//console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
//snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild);
//SendConsoleEvent(dwEvent, idObject, idChild);
break;
case EVENT_CONSOLE_END_APPLICATION:
// A console process has exited
if (idObject.Val == this.terminal._hProcessID)
{
//console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
@ -436,44 +360,18 @@ function windows_terminal() {
return (ret);
}
// Retrieves a message from the calling thread's message queue
this._GetMessage = function ()
{
//
// Reference for GetMessage() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
//
//
// Reference for TranslateMessage() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-translatemessage
//
//
// Reference for DispatchMessage() can be found at:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dispatchmessage
//
this._GetMessage = function () {
if (this._user32.abort) { console.log('aborting loop'); return; }
this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret)
{
this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret) {
//console.log('GetMessage Response');
if (ret.Val != 0)
{
if (ret.Val == -1)
{
if (ret.Val != 0) {
if (ret.Val == -1) {
// handle the error and possibly exit
}
else
{
// Translates virtual-key messages into character messages
} else {
//console.log('TranslateMessage');
this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
{
// Dispatches a message to a window procedure
this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () {
//console.log('DispatchMessage');
this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
{
this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () {
this.nativeProxy.terminal._GetMessage();
}, console.log);
}, console.log);
@ -486,8 +384,7 @@ function windows_terminal() {
if (this.nativeProxy.terminal._hProcess == null) { return; }
this.nativeProxy.terminal.stopping._res();
if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0)
{
if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0) {
var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
console.log('Unable to kill Terminal Process, error: ' + e);
}
@ -497,38 +394,22 @@ function windows_terminal() {
console.log('REJECTED_UnhookWinEvent: ' + err);
});
}
}, function (err)
{
}, function (err) {
// Get Message Failed
console.log('REJECTED_GETMessage: ' + err);
});
}
this._WriteBuffer = function (buf)
{
for (var i = 0; i < buf.length; ++i)
{
if (typeof (buf) == 'string')
{
this._WriteBuffer = function (buf) {
for (var i = 0; i < buf.length; ++i) {
if (typeof (buf) == 'string') {
this._WriteCharacter(buf.charCodeAt(i), false);
} else
{
} else {
this._WriteCharacter(buf[i], false);
}
}
}
this._WriteCharacter = function (key, bControlKey)
{
//
// Reference for WriteConsoleInput() can be found at:
// https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
//
//
// Reference for INPUT_RECORD can be found at:
// https://learn.microsoft.com/en-us/windows/console/input-record-str
//
var rec = GM.CreateVariable(20);
rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
@ -546,78 +427,62 @@ function windows_terminal() {
}
// Get the current visible screen buffer
this._GetScreenBuffer = function (sx, sy, ex, ey)
{
//
// Reference for GetConsoleScreenBufferInfo() can be found at:
// https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
//
//
// Reference for ReadConsoleOutput() can be found at:
// https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
//
this._GetScreenBuffer = function (sx, sy, ex, ey) {
var info = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
if (arguments[3] == null)
{
if (arguments[3] == null) {
// Use Default Parameters
sx = 0;
sy = 0;
ex = nWidth - 1;
ey = nHeight - 1;
} else
{
} else {
if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; }
if (this._scry != 0) { sy += this._scry; ey += this._scry; }
this._scrx = this._scry = 0;
}
var nBuffer = GM.CreateVariable((ex - sx + 1) * (ey - sy + 1) * 4);
var size = GM.CreateVariable(4);
size.Deref(0, 2).toBuffer().writeUInt16LE(ex - sx + 1, 0);
size.Deref(2, 2).toBuffer().writeUInt16LE(ey - sy + 1, 0);
var startCoord = GM.CreateVariable(4);
startCoord.Deref(0, 2).toBuffer().writeUInt16LE(0, 0);
startCoord.Deref(2, 2).toBuffer().writeUInt16LE(0, 0);
var region = GM.CreateVariable(8);
region.buffer = region.toBuffer();
region.buffer.writeUInt16LE(sx, 0);
region.buffer.writeUInt16LE(sy, 2);
region.buffer.writeUInt16LE(ex, 4);
region.buffer.writeUInt16LE(ey, 6);
if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0)
{
if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0) {
throw ('Unable to read Console Output');
}
// Lets convert the buffer into something simpler
//var retVal = { data: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), attributes: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), width: dw - dx + 1, height: dh - dy + 1, x: dx, y: dy };
var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy };
var x, y, line, ifo, tmp, lineWidth = ex - sx + 1;
for (y = 0; y <= (ey - sy) ; ++y)
{
for (y = 0; y <= (ey - sy) ; ++y) {
retVal.data.push(Buffer.alloc(lineWidth));
retVal.attributes.push(Buffer.alloc(lineWidth));
line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer();
for (x = 0; x < lineWidth; ++x)
{
for (x = 0; x < lineWidth; ++x) {
retVal.data.peek()[x] = line[x * 4];
retVal.attributes.peek()[x] = line[2 + (x * 4)];
}
}
return (retVal);
}
@ -642,11 +507,6 @@ function windows_terminal() {
this._SendScroll = function _SendScroll(dx, dy)
{
//
// Reference for GetConsoleScreenBufferInfo() can be found at:
// https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
//
if (this._scrollTimer || this._stream == null) { return; }
var info = GM.CreateVariable(22);
@ -686,15 +546,12 @@ function LOWORD(val) { return (val & 0xFFFF); }
function HIWORD(val) { return ((val >> 16) & 0xFFFF); }
function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); }
function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); }
function TranslateLine(x, y, data, attributes)
{
function TranslateLine(x, y, data, attributes) {
var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])];
if (typeof attributes == 'number') { attributes = [attributes]; } // If we get a single attribute, turn it into an array.
if (typeof attributes == 'number') { attributes = [ attributes ]; } // If we get a single attribute, turn it into an array.
for (i = 0; i < data.length; i++)
{
if (lastAttr != attributes[i])
{ // To boost performance, if the attribute is the same as the last one, skip this entire part.
for (i = 0; i < data.length; i++) {
if (lastAttr != attributes[i]) { // To boost performance, if the attribute is the same as the last one, skip this entire part.
fc = (attributes[i] & 0x0007);
fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color
bc = (attributes[i] & 0x0070) >> 4;
@ -702,19 +559,19 @@ function TranslateLine(x, y, data, attributes)
rc = (attributes[i] & 0x4000); // Reverse color set
fb = (attributes[i] & 0x0008) >> 3; // Bright foreground set
bb = (attributes[i] & 0x0080); // Bright background set
if (rc != rcolor) { if (rc != 0) { esc.push(7); } else { esc.push(0); fcolor = 7; bcolor = 0; fbright = 0; bbright = 0; } rcolor = rc; } // Reverse Color
if (fc != fcolor) { esc.push(fc + 30); fcolor = fc; } // Set the foreground color if needed
if (bc != bcolor) { esc.push(bc + 40); bcolor = bc; } // Set the background color if needed
if (fb != fbright) { esc.push(2 - fb); fbright = fb; } // Set the bright foreground color if needed
if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed
if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed
if (esc.length > 0) { output.push(GetEsc('m', esc)); esc = []; }
lastAttr = attributes[i];
}
output.push(Buffer.from(String.fromCharCode(data[i])));
}
return Buffer.concat(output);
}

View file

@ -47,20 +47,20 @@ function vt()
var GM = require('_GenericMarshal');
var k32 = GM.CreateNativeProxy('kernel32.dll');
k32.CreateMethod('CancelIoEx'); // https://learn.microsoft.com/en-us/windows/win32/fileio/cancelioex-func
k32.CreateMethod('CreatePipe'); // https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
k32.CreateMethod('CreateProcessW'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
k32.CreateMethod('CreatePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/createpseudoconsole
k32.CreateMethod('CloseHandle'); // https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
k32.CreateMethod('ClosePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/closepseudoconsole
k32.CreateMethod('GetProcessHeap'); // https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-getprocessheap
k32.CreateMethod('HeapAlloc'); // https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc
k32.CreateMethod('InitializeProcThreadAttributeList'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist
k32.CreateMethod('ResizePseudoConsole'); // https://learn.microsoft.com/en-us/windows/console/resizepseudoconsole
k32.CreateMethod('UpdateProcThreadAttribute'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute
k32.CreateMethod('WriteFile'); // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
k32.CreateMethod('ReadFile'); // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
k32.CreateMethod('TerminateProcess'); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess
k32.CreateMethod('CancelIoEx');
k32.CreateMethod('CreatePipe');
k32.CreateMethod('CreateProcessW');
k32.CreateMethod('CreatePseudoConsole');
k32.CreateMethod('CloseHandle');
k32.CreateMethod('ClosePseudoConsole');
k32.CreateMethod('GetProcessHeap');
k32.CreateMethod('HeapAlloc');
k32.CreateMethod('InitializeProcThreadAttributeList');
k32.CreateMethod('ResizePseudoConsole');
k32.CreateMethod('UpdateProcThreadAttribute');
k32.CreateMethod('WriteFile');
k32.CreateMethod('ReadFile');
k32.CreateMethod('TerminateProcess');
var ret = { _h: GM.CreatePointer(), _consoleInput: GM.CreatePointer(), _consoleOutput: GM.CreatePointer(), _input: GM.CreatePointer(), _output: GM.CreatePointer(), k32: k32 };
var attrSize = GM.CreateVariable(8);
@ -77,31 +77,18 @@ function vt()
throw ('Error calling CreatePseudoConsole()');
}
//
// Reference for STARTUPINFOEXW
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-startupinfoexw
//
//
// Reference for STARTUPINFOW
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow
//
k32.InitializeProcThreadAttributeList(0, 1, 0, attrSize);
attrList = GM.CreateVariable(attrSize.toBuffer().readUInt32LE());
var startupinfoex = GM.CreateVariable(GM.PointerSize == 8 ? 112 : 72); // Create Structure, 64 bits is 112 bytes, 32 bits is 72 bytes
startupinfoex.toBuffer().writeUInt32LE(GM.PointerSize == 8 ? 112 : 72, 0); // Write buffer size
attrList.pointerBuffer().copy(startupinfoex.Deref(GM.PointerSize == 8 ? 104 : 68, GM.PointerSize).toBuffer()); // Write the reference to STARTUPINFOEX
var startupinfoex = GM.CreateVariable(GM.PointerSize == 8 ? 112 : 72);
startupinfoex.toBuffer().writeUInt32LE(GM.PointerSize == 8 ? 112 : 72, 0);
attrList.pointerBuffer().copy(startupinfoex.Deref(GM.PointerSize == 8 ? 104 : 68, GM.PointerSize).toBuffer());
if (k32.InitializeProcThreadAttributeList(attrList, 1, 0, attrSize).Val != 0)
{
if (k32.UpdateProcThreadAttribute(attrList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, ret._h.Deref(), GM.PointerSize, 0, 0).Val != 0)
{
if (k32.CreateProcessW(0, GM.CreateVariable(path, { wide: true }), 0, 0, 1, EXTENDED_STARTUPINFO_PRESENT, 0, 0, startupinfoex, pi).Val != 0) // Create the process to run in the pseudoconsole
if (k32.CreateProcessW(0, GM.CreateVariable(path, { wide: true }), 0, 0, 1, EXTENDED_STARTUPINFO_PRESENT, 0, 0, startupinfoex, pi).Val != 0)
{
//
// Create a Stream Object, to be able to read/write data to the pseudoconsole
//
ret._startupinfoex = startupinfoex;
ret._process = pi.Deref(0);
ret._pid = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE();
@ -124,10 +111,6 @@ function vt()
flush();
}
});
//
// The ProcessInfo object is signaled when the process exits
//
ds._obj = ret;
ret._waiter = require('DescriptorEvents').addDescriptor(pi.Deref(0));
ret._waiter.ds = ds;
@ -168,7 +151,6 @@ function vt()
ds._rpbufRead = GM.CreateVariable(4);
ds.__read = function __read()
{
// Asyncronously read data from the pseudoconsole
this._rp = this.terminal.k32.ReadFile.async(this.terminal._output.Deref(), this._rpbuf, this._rpbuf._size, this._rpbufRead, 0);
this._rp.then(function ()
{
@ -191,8 +173,6 @@ function vt()
}
throw ('Internal Error');
}
// This evaluates whether or not the powershell binary exists
this.PowerShellCapable = function ()
{
if (require('os').arch() == 'x64')
@ -204,14 +184,10 @@ function vt()
return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
}
}
// Start the PseudoConsole with the Command Prompt
this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
{
return (this.Create(process.env['windir'] + '\\System32\\cmd.exe', CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT));
}
// Start the PseduoConsole with PowerShell
this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
{
if (require('os').arch() == 'x64')

View file

@ -39,90 +39,17 @@ function getVolumes()
{
ret[v[i].DeviceID] = trimObject(v[i]);
}
try {
v = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftVolumeEncryption', 'SELECT * FROM Win32_EncryptableVolume');
for (i in v)
v = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftVolumeEncryption', 'SELECT * FROM Win32_EncryptableVolume');
for (i in v)
{
var tmp = trimObject(v[i]);
for (var k in tmp)
{
var tmp = trimObject(v[i]);
for (var k in tmp)
{
ret[tmp.DeviceID][k] = tmp[k];
}
ret[tmp.DeviceID][k] = tmp[k];
}
} catch (ex) { }
}
return (ret);
}
function windows_volumes()
{
var promise = require('promise');
var p1 = new promise(function (res, rej) { this._res = res; this._rej = rej; });
var ret = {};
var values = require('win-wmi').query('ROOT\\CIMV2', 'SELECT * FROM Win32_LogicalDisk', ['DeviceID', 'VolumeName', 'FileSystem', 'Size', 'FreeSpace', 'DriveType']);
if(values[0]){
for (var i = 0; i < values.length; ++i) {
var drive = values[i]['DeviceID'].slice(0,-1);
ret[drive] = {
name: (values[i]['VolumeName'] ? values[i]['VolumeName'] : ""),
type: (values[i]['FileSystem'] ? values[i]['FileSystem'] : "Unknown"),
size: (values[i]['Size'] ? values[i]['Size'] : 0),
sizeremaining: (values[i]['FreeSpace'] ? values[i]['FreeSpace'] : 0),
removable: (values[i]['DriveType'] == 2),
cdrom: (values[i]['DriveType'] == 5)
};
}
}
try {
values = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftVolumeEncryption', 'SELECT * FROM Win32_EncryptableVolume', ['DriveLetter','ConversionStatus','ProtectionStatus']);
if(values[0]){
for (var i = 0; i < values.length; ++i) {
var drive = values[i]['DriveLetter'].slice(0,-1);
var statuses = {
0: 'FullyDecrypted',
1: 'FullyEncrypted',
2: 'EncryptionInProgress',
3: 'DecryptionInProgress',
4: 'EncryptionPaused',
5: 'DecryptionPaused'
};
ret[drive].volumeStatus = statuses.hasOwnProperty(values[i].ConversionStatus) ? statuses[values[i].ConversionStatus] : 'FullyDecrypted';
ret[drive].protectionStatus = (values[i].ProtectionStatus == 0 ? 'Off' : (values[i].ProtectionStatus == 1 ? 'On' : 'Unknown'));
try {
var foundIDMarkedLine = false, foundMarkedLine = false, identifier = '', password = '';
var keychild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'manage-bde -protectors -get ' + drive + ': -Type recoverypassword'], {});
keychild.stdout.str = ''; keychild.stdout.on('data', function (c) { this.str += c.toString(); });
keychild.waitExit();
var lines = keychild.stdout.str.trim().split('\r\n');
for (var x = 0; x < lines.length; x++) { // Loop each line
var abc = lines[x].trim();
var englishidpass = (abc !== '' && abc.includes('Numerical Password:')); // English ID
var germanidpass = (abc !== '' && abc.includes('Numerisches Kennwort:')); // German ID
var frenchidpass = (abc !== '' && abc.includes('Mot de passe num')); // French ID
var englishpass = (abc !== '' && abc.includes('Password:') && !abc.includes('Numerical Password:')); // English Password
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) {
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();
foundIDMarkedLine = true;
}else if (x + 1 < lines.length && nextline !== '') {
password = nextline;
foundMarkedLine = true;
}
}
}
ret[drive].identifier = (foundIDMarkedLine ? identifier : ''); // Set Bitlocker Identifier
ret[drive].recoveryPassword = (foundMarkedLine ? password : ''); // Set Bitlocker Password
} catch(ex) { } // just carry on as we cant get bitlocker key
}
}
p1._res(ret);
} catch (ex) { p1._res(ret); } // just return volumes as cant get encryption/bitlocker
return (p1);
}
module.exports = {
getVolumes: function () { try { return (getVolumes()); } catch (x) { return ({}); } },
volumes_promise: windows_volumes
};
module.exports = { getVolumes: function () { try { return (getVolumes()); } catch (x) { return ({}); } } };

View file

@ -485,8 +485,8 @@ function windows_execve(name, agentfilename, sessionid) {
var cmd = require('_GenericMarshal').CreateVariable(process.env['windir'] + '\\system32\\cmd.exe', { wide: true });
var args = require('_GenericMarshal').CreateVariable(3 * require('_GenericMarshal').PointerSize);
var arg1 = require('_GenericMarshal').CreateVariable('cmd.exe', { wide: true });
var arg2 = require('_GenericMarshal').CreateVariable('/C net stop "' + name + '" & "' + cwd + agentfilename + '.update.exe" -b64exec ' + 'dHJ5CnsKICAgIHZhciBzZXJ2aWNlTG9jYXRpb24gPSBwcm9jZXNzLmFyZ3YucG9wKCkudG9Mb3dlckNhc2UoKTsKICAgIHJlcXVpcmUoJ3Byb2Nlc3MtbWFuYWdlcicpLmVudW1lcmF0ZVByb2Nlc3NlcygpLnRoZW4oZnVuY3Rpb24gKHByb2MpCiAgICB7CiAgICAgICAgZm9yICh2YXIgcCBpbiBwcm9jKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKHByb2NbcF0ucGF0aCAmJiAocHJvY1twXS5wYXRoLnRvTG93ZXJDYXNlKCkgPT0gc2VydmljZUxvY2F0aW9uKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcHJvY2Vzcy5raWxsKHByb2NbcF0ucGlkKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBwcm9jZXNzLmV4aXQoKTsKICAgIH0pOwp9CmNhdGNoIChlKQp7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQ==' +
' "' + process.execPath + '" & copy "' + cwd + agentfilename + '.update.exe" "' + process.execPath + '" & net start "' + name + '" & erase "' + cwd + agentfilename + '.update.exe"', { wide: true });
var arg2 = require('_GenericMarshal').CreateVariable('/C wmic service "' + name + '" call stopservice & "' + cwd + agentfilename + '.update.exe" -b64exec ' + 'dHJ5CnsKICAgIHZhciBzZXJ2aWNlTG9jYXRpb24gPSBwcm9jZXNzLmFyZ3YucG9wKCkudG9Mb3dlckNhc2UoKTsKICAgIHJlcXVpcmUoJ3Byb2Nlc3MtbWFuYWdlcicpLmVudW1lcmF0ZVByb2Nlc3NlcygpLnRoZW4oZnVuY3Rpb24gKHByb2MpCiAgICB7CiAgICAgICAgZm9yICh2YXIgcCBpbiBwcm9jKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKHByb2NbcF0ucGF0aCAmJiAocHJvY1twXS5wYXRoLnRvTG93ZXJDYXNlKCkgPT0gc2VydmljZUxvY2F0aW9uKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcHJvY2Vzcy5raWxsKHByb2NbcF0ucGlkKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBwcm9jZXNzLmV4aXQoKTsKICAgIH0pOwp9CmNhdGNoIChlKQp7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQ==' +
' "' + process.execPath + '" & copy "' + cwd + agentfilename + '.update.exe" "' + process.execPath + '" & wmic service "' + name + '" call startservice & erase "' + cwd + agentfilename + '.update.exe"', { wide: true });
if (name == null)
{

View file

@ -30,6 +30,7 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
const fs = require('fs');
var obj = {};
obj.debug = false;
obj.protocol = 3; // IDER
obj.bytesToAmt = 0;
obj.bytesFromAmt = 0;
@ -37,7 +38,7 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
obj.tx_timeout = 0; // Default 0
obj.heartbeat = 20000; // Default 20000
obj.version = 1;
obj.acc = null;
obj.acc = "";
obj.inSequence = 0;
obj.outSequence = 0;
obj.iderinfo = null;
@ -45,50 +46,47 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
obj.floppy = null;
obj.cdrom = null;
obj.floppySize = null;
obj.cdromSize = null;
obj.floppyReady = false;
obj.cdromReady = false;
//obj.pingTimer = null;
obj.sectorStats = null;
obj.debug = false;
// Private method
function debug() { if (obj.debug) { console.log(...arguments); } }
// Mode Sense
var IDE_ModeSence_LS120Disk_Page_Array = Buffer.from([0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00]);
var IDE_ModeSence_3F_LS120_Array = Buffer.from([0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31]);
var IDE_ModeSence_FloppyDisk_Page_Array = Buffer.from([0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00]);
var IDE_ModeSence_3F_Floppy_Array = Buffer.from([0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31]);
var IDE_ModeSence_CD_1A_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
//var IDE_ModeSence_CD_1B_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
var IDE_ModeSence_CD_1D_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
var IDE_ModeSence_CD_2A_Array = Buffer.from([0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
//var IDE_ModeSence_CD_01_Array = Buffer.from([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00]);
var IDE_ModeSence_3F_CD_Array = Buffer.from([0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
var IDE_ModeSence_LS120Disk_Page_Array = String.fromCharCode(0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00);
var IDE_ModeSence_3F_LS120_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31);
var IDE_ModeSence_FloppyDisk_Page_Array = String.fromCharCode(0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00);
var IDE_ModeSence_3F_Floppy_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31);
var IDE_ModeSence_CD_1A_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
//var IDE_ModeSence_CD_1B_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
var IDE_ModeSence_CD_1D_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
var IDE_ModeSence_CD_2A_Array = String.fromCharCode(0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
//var IDE_ModeSence_CD_01_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00);
var IDE_ModeSence_3F_CD_Array = String.fromCharCode(0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
// 0x46 constant data
var IDE_CD_ConfigArrayHeader = Buffer.from([0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08]);
var IDE_CD_ConfigArrayProfileList = Buffer.from([0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00]);
var IDE_CD_ConfigArrayCore = Buffer.from([0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02]);
var IDE_CD_Morphing = Buffer.from([0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00]);
var IDE_CD_ConfigArrayRemovable = Buffer.from([0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02]);
var IDE_CD_ConfigArrayRandom = Buffer.from([0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00]);
var IDE_CD_Read = Buffer.from([0x00, 0x1E, 0x03, 0x00]);
var IDE_CD_PowerManagement = Buffer.from([0x01, 0x00, 0x03, 0x00]);
var IDE_CD_Timeout = Buffer.from([0x01, 0x05, 0x03, 0x00]);
var IDE_CD_ConfigArrayHeader = String.fromCharCode(0x00, 0x00,0x00, 0x28, 0x00, 0x00, 0x00, 0x08);
var IDE_CD_ConfigArrayProfileList = String.fromCharCode(0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00);
var IDE_CD_ConfigArrayCore = String.fromCharCode(0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02);
var IDE_CD_Morphing = String.fromCharCode(0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00);
var IDE_CD_ConfigArrayRemovable = String.fromCharCode(0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02);
var IDE_CD_ConfigArrayRandom = String.fromCharCode(0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00);
var IDE_CD_Read = String.fromCharCode(0x00, 0x1E, 0x03, 0x00);
var IDE_CD_PowerManagement = String.fromCharCode(0x01, 0x00, 0x03, 0x00);
var IDE_CD_Timeout = String.fromCharCode(0x01, 0x05, 0x03, 0x00);
// 0x01 constant data
var IDE_ModeSence_FloppyError_Recovery_Array = Buffer.from([0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
var IDE_ModeSence_Ls120Error_Recovery_Array = Buffer.from([0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
var IDE_ModeSence_CDError_Recovery_Array = Buffer.from([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00]);
var IDE_ModeSence_FloppyError_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
var IDE_ModeSence_Ls120Error_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
var IDE_ModeSence_CDError_Recovery_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00);
// CD info and performance
var RD_CD_DiskInfo = Buffer.from([0x00, 0x20, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x20, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
var RD_CD_Performance = Buffer.from([0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00]);
// Private method, called by parent when it change state
obj.xxStateChange = function (newstate) {
if (obj.debug) console.log("IDER-StateChange", newstate);
debug("IDER-StateChange", newstate);
if (newstate == 0) { obj.Stop(); }
if (newstate == 3) { obj.Start(); }
}
@ -102,7 +100,8 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
if (fs.existsSync(floppyPath) == false) { return 1; } // Floppy disk image does not exist
var stats = fs.statSync(floppyPath);
if ((stats.size % 512) != 0) { return 2; } // Invalid floppy disk image
obj.floppy = { size: stats.size, ptr: fs.openSync(floppyPath, 'r') };
obj.floppySize = stats.size;
obj.floppy = fs.openSync(floppyPath, 'r');
} catch (ex) { return 3; } // Unable to open floppy disk image
}
@ -112,7 +111,8 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
if (fs.existsSync(cdromPath) == false) { return 4; } // CDROM disk image does not exist
var stats = fs.statSync(cdromPath);
if ((stats.size % 512) != 0) { return 5; } // Invalid CDROM disk image
obj.cdrom = { size: stats.size, ptr: fs.openSync(cdromPath, 'r') };
obj.cdromSize = stats.size;
obj.cdrom = fs.openSync(cdromPath, 'r');
} catch (ex) { return 6; } // Unable to open CDROM disk image
}
@ -122,7 +122,9 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
}
obj.Start = function () {
if (obj.debug) { console.log('IDER-Start'); console.log(obj.floppy, obj.cdrom); }
debug("IDER-Start");
// Get ready
obj.bytesToAmt = 0;
obj.bytesFromAmt = 0;
obj.inSequence = 0;
@ -130,80 +132,87 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
g_readQueue = [];
// Send first command, OPEN_SESSION
obj.SendCommand(0x40, Buffer.concat([ShortToStrX(obj.rx_timeout), ShortToStrX(obj.tx_timeout), ShortToStrX(obj.heartbeat), IntToStrX(obj.version)]));
obj.SendCommand(0x40, webserver.common.ShortToStrX(obj.rx_timeout) + webserver.common.ShortToStrX(obj.tx_timeout) + webserver.common.ShortToStrX(obj.heartbeat) + webserver.common.IntToStrX(obj.version));
// Send sector stats
if (obj.sectorStats) {
obj.sectorStats(0, 0, obj.floppy ? (obj.floppy.size >> 9) : 0);
obj.sectorStats(0, 1, obj.cdrom ? (obj.cdrom.size >> 11) : 0);
obj.sectorStats(0, 0, obj.floppy ? (obj.floppySize >> 9) : 0);
obj.sectorStats(0, 1, obj.cdrom ? (obj.cdromSize >> 11) : 0);
}
// Setup the ping timer
//obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
}
obj.Stop = function () {
if (obj.debug) console.log('IDER-Stop');
//if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
debug("IDER-Stop");
// Close the files
if (obj.floppy) { fs.close(obj.floppy); delete obj.floppy; }
if (obj.cdrom) { fs.close(obj.cdrom); delete obj.cdrom; }
// Clean up
obj.floppySize = 0;
obj.cdromSize = 0;
obj.floppyReady = false;
obj.cdromReady = false;
// Stop the redirection connection
obj.parent.Stop();
}
// Private method
obj.ProcessData = function (data) {
data = Buffer.from(data, 'binary');
obj.bytesFromAmt += data.length;
if (obj.acc == null) { obj.acc = data; } else { obj.acc = Buffer.concat([obj.acc, data]); }
if (obj.debug) console.log('IDER-ProcessData', obj.acc.length, obj.acc.toString('hex'));
obj.acc += data;
debug('IDER-ProcessData', obj.acc.length, webserver.common.rstr2hex(obj.acc));
// Process as many commands as possible
while (obj.acc != null) {
while (true) {
var len = obj.ProcessDataEx();
if (len == 0) return;
if (obj.inSequence != ReadIntX(obj.acc, 4)) {
if (obj.debug) console.log('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4));
if (obj.inSequence != webserver.common.ReadIntX(obj.acc, 4)) {
debug('ERROR: Out of sequence', obj.inSequence, webserver.common.ReadIntX(obj.acc, 4));
obj.Stop();
return;
}
obj.inSequence++;
if (len == obj.acc.length) { obj.acc = null; } else { obj.acc = obj.acc.slice(len); }
obj.acc = obj.acc.substring(len);
}
}
// Private method
obj.SendCommand = function (cmdid, data, completed, dma) {
if (data == null) { data = Buffer.alloc(0); }
if (data == null) { data = ''; }
var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
if (dma) { attributes += 1; }
var x = Buffer.concat([Buffer.from([cmdid, 0, 0, attributes]), IntToStrX(obj.outSequence++), data]);
var x = String.fromCharCode(cmdid, 0, 0, attributes) + webserver.common.IntToStrX(obj.outSequence++) + data;
obj.parent.xxSend(x);
obj.bytesToAmt += x.length;
//if (cmdid != 0x4B) { console.log('IDER-SendData', x.length, x.toString('hex')); }
if (cmdid != 0x4B) { debug('IDER-SendData', x.length, webserver.common.rstr2hex(x)); }
}
// CommandEndResponse (SCSI_SENSE)
obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
if (error) { obj.SendCommand(0x51, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0]), true); }
else { obj.SendCommand(0x51, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq]), true); }
if (error) { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0), true); }
else { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq), true); }
}
// DataToHost (SCSI_READ)
obj.SendDataToHost = function (device, completed, data, dma) {
var dmalen = (dma) ? 0 : data.length;
if (completed == true) {
obj.SendCommand(0x54, Buffer.concat([Buffer.from([0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0]), data]), completed, dma);
obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0) + data, completed, dma);
} else {
obj.SendCommand(0x54, Buffer.concat([Buffer.from([0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), data]), completed, dma);
obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + data, completed, dma);
}
}
// GetDataFromHost (SCSI_CHUNK)
obj.SendGetDataFromHost = function (device, chunksize) {
obj.SendCommand(0x52, Buffer.from([0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), false);
obj.SendCommand(0x52, String.fromCharCode(0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), false);
}
// DisableEnableFeatures (STATUS_DATA)
// If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, Buffer.concat([Buffer.from([type]), data])); }
obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, String.fromCharCode(type) + data); }
// Private method
obj.ProcessDataEx = function () {
@ -211,128 +220,130 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
// First 8 bytes are the header
// CommandID + 0x000000 + Sequence Number
//console.log('ProcessDataEx', obj.acc[0], obj.acc);
switch (obj.acc[0]) {
switch(obj.acc.charCodeAt(0)) {
case 0x41: // OPEN_SESSION
if (obj.acc.length < 30) return 0;
var len = obj.acc[29];
var len = obj.acc.charCodeAt(29);
if (obj.acc.length < (30 + len)) return 0;
obj.iderinfo = {};
obj.iderinfo.major = obj.acc[8];
obj.iderinfo.minor = obj.acc[9];
obj.iderinfo.fwmajor = obj.acc[10];
obj.iderinfo.fwminor = obj.acc[11];
obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
obj.iderinfo.proto = obj.acc[21];
obj.iderinfo.iana = ReadIntX(obj.acc, 25);
if (obj.debug) console.log(obj.iderinfo);
obj.iderinfo.major = obj.acc.charCodeAt(8);
obj.iderinfo.minor = obj.acc.charCodeAt(9);
obj.iderinfo.fwmajor = obj.acc.charCodeAt(10);
obj.iderinfo.fwminor = obj.acc.charCodeAt(11);
obj.iderinfo.readbfr = webserver.common.ReadShortX(obj.acc, 16);
obj.iderinfo.writebfr = webserver.common.ReadShortX(obj.acc, 18);
obj.iderinfo.proto = obj.acc.charCodeAt(21);
obj.iderinfo.iana = webserver.common.ReadIntX(obj.acc, 25);
debug(obj.iderinfo);
if (obj.iderinfo.proto != 0) {
if (obj.debug) console.log("Unknown proto", obj.iderinfo.proto);
debug("Unknown proto", obj.iderinfo.proto);
obj.Stop();
}
if (obj.iderinfo.readbfr > 8192) {
if (obj.debug) console.log("Illegal read buffer size", obj.iderinfo.readbfr);
debug("Illegal read buffer size", obj.iderinfo.readbfr);
obj.Stop();
}
if (obj.iderinfo.writebfr > 8192) {
if (obj.debug) console.log("Illegal write buffer size", obj.iderinfo.writebfr);
debug("Illegal write buffer size", obj.iderinfo.writebfr);
obj.Stop();
}
if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, webserver.common.IntToStrX(0x01 + 0x08)); } // OnReboot
else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, webserver.common.IntToStrX(0x01 + 0x10)); } // Graceful
else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, webserver.common.IntToStrX(0x01 + 0x18)); } // Now
//obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
return 30 + len;
case 0x43: // CLOSE
if (obj.debug) console.log('CLOSE');
debug('CLOSE');
obj.Stop();
return 8;
case 0x44: // KEEPALIVEPING
obj.SendCommand(0x45); // Send PONG back
return 8;
case 0x45: // KEEPALIVEPONG
if (obj.debug) console.log('PONG');
debug('PONG');
return 8;
case 0x46: // RESETOCCURED
if (obj.acc.length < 9) return 0;
var resetMask = obj.acc[8];
var resetMask = obj.acc.charCodeAt(8);
if (g_media === null) {
// No operations are pending
obj.SendCommand(0x47); // Send ResetOccuredResponse
if (obj.debug) console.log('RESETOCCURED1', resetMask);
debug('RESETOCCURED1', resetMask);
} else {
// Operations are being done, sent the reset once completed.
g_reset = true;
if (obj.debug) console.log('RESETOCCURED2', resetMask);
debug('RESETOCCURED2', resetMask);
}
return 9;
case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
if (obj.acc.length < 13) return 0;
var type = obj.acc[8];
var value = ReadIntX(obj.acc, 9);
if (obj.debug) console.log('STATUS_DATA', type, value);
switch (type) {
var type = obj.acc.charCodeAt(8);
var value = webserver.common.ReadIntX(obj.acc, 9);
debug('STATUS_DATA', type, value);
switch (type)
{
case 1: // REGS_AVAIL
if (value & 1) {
if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, webserver.common.IntToStrX(0x01 + 0x08)); } // OnReboot
else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, webserver.common.IntToStrX(0x01 + 0x10)); } // Graceful
else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, webserver.common.IntToStrX(0x01 + 0x18)); } // Now
}
break;
case 2: // REGS_STATUS
obj.enabled = (value & 2) ? true : false;
if (obj.debug) console.log("IDER Status: " + obj.enabled);
debug("IDER Status: " + obj.enabled);
break;
case 3: // REGS_TOGGLE
if (value != 1) {
if (obj.debug) console.log("Register toggle failure");
debug("Register toggle failure");
} //else { obj.SendDisableEnableFeatures(2); }
break;
}
return 13;
case 0x4A: // ERROR OCCURED
if (obj.acc.length < 11) return 0;
if (obj.debug) console.log('IDER: ABORT', obj.acc[8]);
debug('IDER: ABORT', obj.acc.charCodeAt(8));
//obj.Stop();
return 11;
case 0x4B: // HEARTBEAT
//console.log('HEARTBEAT');
//debug('HEARTBEAT');
return 8;
case 0x50: // COMMAND WRITTEN
if (obj.acc.length < 28) return 0;
var device = (obj.acc[14] & 0x10) ? 0xB0 : 0xA0;
var deviceFlags = obj.acc[14];
var cdb = obj.acc.slice(16, 28);
var featureRegister = obj.acc[9];
if (obj.debug) console.log('SCSI_CMD', device, cdb.toString('hex'), featureRegister, deviceFlags);
var device = (obj.acc.charCodeAt(14) & 0x10) ? 0xB0 : 0xA0;
var deviceFlags = obj.acc.charCodeAt(14);
var cdb = obj.acc.substring(16, 28);
var featureRegister = obj.acc.charCodeAt(9);
debug('SCSI_CMD', device, webserver.common.rstr2hex(cdb), featureRegister, deviceFlags);
handleSCSI(device, cdb, featureRegister, deviceFlags);
return 28;
case 0x53: // DATA FROM HOST
if (obj.acc.length < 14) return 0;
var len = ReadShortX(obj.acc, 9);
var len = webserver.common.ReadShortX(obj.acc, 9);
if (obj.acc.length < (14 + len)) return 0;
if (obj.debug) console.log('SCSI_WRITE, len = ' + (14 + len));
obj.SendCommand(0x51, Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00]), true);
debug('SCSI_WRITE, len = ' + (14 + len));
obj.SendCommand(0x51, String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00), true);
return 14 + len;
default:
if (obj.debug) console.log('Unknown IDER command', obj.acc[0]);
debug('Unknown IDER command', obj.acc[0]);
obj.Stop();
break;
}
return 0;
}
function handleSCSI(dev, cdb, featureRegister, deviceFlags) {
function handleSCSI(dev, cdb, featureRegister, deviceFlags)
{
var lba;
var len;
switch (cdb[0]) {
switch(cdb.charCodeAt(0))
{
case 0x00: // TEST_UNIT_READY:
if (obj.debug) console.log("SCSI: TEST_UNIT_READY", dev);
debug("SCSI: TEST_UNIT_READY", dev);
switch (dev) {
case 0xA0: // DEV_FLOPPY
if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
@ -343,34 +354,34 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
break;
default:
if (obj.debug) console.log("SCSI Internal error 3", dev);
debug("SCSI Internal error 3", dev);
return -1;
}
obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
break;
case 0x08: // READ_6
lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
len = cdb[4];
lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
len = cdb.charCodeAt(4);
if (len == 0) { len = 256; }
if (obj.debug) console.log("SCSI: READ_6", dev, lba, len);
debug("SCSI: READ_6", dev, lba, len);
sendDiskData(dev, lba, len, featureRegister);
break;
case 0x0a: // WRITE_6
lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
len = cdb[4];
lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
len = cdb.charCodeAt(4);
if (len == 0) { len = 256; }
if (obj.debug) console.log("SCSI: WRITE_6", dev, lba, len);
debug("SCSI: WRITE_6", dev, lba, len);
obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
return -1;
/*
case 0x15: // MODE_SELECT_6:
console.log("SCSI ERROR: MODE_SELECT_6", dev);
obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
return -1;
*/
/*
case 0x15: // MODE_SELECT_6:
debug("SCSI ERROR: MODE_SELECT_6", dev);
obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
return -1;
*/
case 0x1a: // MODE_SENSE_6
if (obj.debug) console.log("SCSI: MODE_SENSE_6", dev);
if ((cdb[2] == 0x3f) && (cdb[3] == 0x00)) {
debug("SCSI: MODE_SENSE_6", dev);
if ((cdb.charCodeAt(2) == 0x3f) && (cdb.charCodeAt(3) == 0x00)) {
var a = 0, b = 0;
switch (dev) {
case 0xA0: // DEV_FLOPPY
@ -384,89 +395,90 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
b = 0x80;
break;
default:
if (obj.debug) console.log("SCSI Internal error 6", dev);
debug("SCSI Internal error 6", dev);
return -1;
}
obj.SendDataToHost(dev, true, Buffer.from([0, a, b, 0]), featureRegister & 1);
obj.SendDataToHost(dev, true, String.fromCharCode(0, a, b, 0), featureRegister & 1);
return;
}
obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
break;
case 0x1b: // START_STOP (Called when you eject the CDROM)
//var immediate = cdb[1] & 0x01;
//var loej = cdb[4] & 0x02;
//var start = cdb[4] & 0x01;
//var immediate = cdb.charCodeAt(1) & 0x01;
//var loej = cdb.charCodeAt(4) & 0x02;
//var start = cdb.charCodeAt(4) & 0x01;
obj.SendCommandEndResponse(1, 0, dev);
break;
case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
if (obj.debug) console.log("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
debug("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
break;
case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
if (obj.debug) console.log("SCSI: READ_FORMAT_CAPACITIES", dev);
var buflen = ReadShort(cdb, 7);
debug("SCSI: READ_FORMAT_CAPACITIES", dev);
var buflen = webserver.common.ReadShort(cdb, 7);
var mediaStatus = 0, sectors;
var mcSize = buflen / 8; // Capacity descriptor size is 8
switch (dev) {
case 0xA0: // DEV_FLOPPY
if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
sectors = (obj.floppy.size >> 9) - 1;
if ((obj.floppy == null) || (obj.floppySize == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
sectors = (obj.floppySize >> 9) - 1;
break;
case 0xB0: // DEV_CDDVD
if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
if ((obj.cdrom == null) || (obj.cdromSize == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
sectors = (obj.cdromSize >> 11) - 1; // Number 2048 byte blocks
break;
default:
if (obj.debug) console.log("SCSI Internal error 4", dev);
debug("SCSI Internal error 4", dev);
return -1;
}
obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(8), Buffer.from([0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00])]), featureRegister & 1);
obj.SendDataToHost(dev, true, webserver.common.IntToStr(8) + String.fromCharCode(0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00), featureRegister & 1);
break;
case 0x25: // READ_CAPACITY
if (obj.debug) console.log("SCSI: READ_CAPACITY", dev);
debug("SCSI: READ_CAPACITY", dev);
var len = 0;
switch (dev) {
switch(dev)
{
case 0xA0: // DEV_FLOPPY
if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
if (obj.debug) console.log('DEV_FLOPPY', len); // Number 512 byte blocks
if ((obj.floppy == null) || (obj.floppySize == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.floppy != null) { len = (obj.floppySize >> 9) - 1; }
debug('DEV_FLOPPY', len); // Number 512 byte blocks
break;
case 0xB0: // DEV_CDDVD
if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
if (obj.debug) console.log('DEV_CDDVD', len);
if ((obj.floppy == null) || (obj.floppySize == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
if (obj.cdrom != null) { len = (obj.cdromSize >> 11) - 1; } // Number 2048 byte blocks
debug('DEV_CDDVD', len);
break;
default:
if (obj.debug) console.log("SCSI Internal error 4", dev);
debug("SCSI Internal error 4", dev);
return -1;
}
//if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
if (obj.debug) console.log("SCSI: READ_CAPACITY2", dev, deviceFlags);
obj.SendDataToHost(deviceFlags, true, Buffer.concat([IntToStr(len), Buffer.from([0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0])]), featureRegister & 1);
debug("SCSI: READ_CAPACITY2", dev, deviceFlags);
obj.SendDataToHost(deviceFlags, true, webserver.common.IntToStr(len) + String.fromCharCode(0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0), featureRegister & 1);
break;
case 0x28: // READ_10
lba = ReadInt(cdb, 2);
len = ReadShort(cdb, 7);
if (obj.debug) console.log("SCSI: READ_10", dev, lba, len);
lba = webserver.common.ReadInt(cdb, 2);
len = webserver.common.ReadShort(cdb, 7);
debug("SCSI: READ_10", dev, lba, len);
sendDiskData(dev, lba, len, featureRegister);
break;
case 0x2a: // WRITE_10 (Floppy only)
case 0x2e: // WRITE_AND_VERIFY (Floppy only)
lba = ReadInt(cdb, 2);
len = ReadShort(cdb, 7);
if (obj.debug) console.log("SCSI: WRITE_10", dev, lba, len);
lba = webserver.common.ReadInt(cdb, 2);
len = webserver.common.ReadShort(cdb, 7);
debug("SCSI: WRITE_10", dev, lba, len);
obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
break;
case 0x43: // READ_TOC (CD Audio only)
var buflen = ReadShort(cdb, 7);
var msf = cdb[1] & 0x02;
var format = cdb[2] & 0x07;
if (format == 0) { format = cdb[9] >> 6; }
if (obj.debug) console.log("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
var buflen = webserver.common.ReadShort(cdb, 7);
var msf = cdb.charCodeAt(1) & 0x02;
var format = cdb.charCodeAt(2) & 0x07;
if (format == 0) { format = cdb.charCodeAt(9) >> 6; }
debug("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
switch (dev) {
case 0xA0: // DEV_FLOPPY
@ -476,100 +488,96 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
// NOP
break;
default:
if (obj.debug) console.log("SCSI Internal error 9", dev);
debug("SCSI Internal error 9", dev);
return -1;
}
if (format == 1) { obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1); }
if (format == 1) { obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1); }
else if (format == 0) {
if (msf) {
obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13]), featureRegister & 1);
obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13), featureRegister & 1);
} else {
obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1);
obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1);
}
}
break;
case 0x46: // GET_CONFIGURATION
var sendall = (cdb[1] != 2);
var firstcode = ReadShort(cdb, 2);
var buflen = ReadShort(cdb, 7);
var sendall = (cdb.charCodeAt(1) != 2);
var firstcode = webserver.common.ReadShort(cdb, 2);
var buflen = webserver.common.ReadShort(cdb, 7);
if (obj.debug) console.log("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
debug("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
if (buflen == 0) { obj.SendDataToHost(dev, true, webserver.common.IntToStr(0x003c) + webserver.common.IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
// Set the header
var r = null;
var r = webserver.common.IntToStr(0x0008);
// Add the data
if (firstcode == 0) { r = IDE_CD_ConfigArrayProfileList; }
if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r = IDE_CD_ConfigArrayCore; }
if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r = IDE_CD_Morphing; }
if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r = IDE_CD_ConfigArrayRemovable; }
if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r = IDE_CD_ConfigArrayRandom; }
if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r = IDE_CD_Read; }
if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r = IDE_CD_PowerManagement; }
if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r = IDE_CD_Timeout; }
if (firstcode == 0) { r += IDE_CD_ConfigArrayProfileList; }
if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r += IDE_CD_ConfigArrayCore; }
if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r += IDE_CD_Morphing; }
if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r += IDE_CD_ConfigArrayRemovable; }
if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r += IDE_CD_ConfigArrayRandom; }
if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r += IDE_CD_Read; }
if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r += IDE_CD_PowerManagement; }
if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r += IDE_CD_Timeout; }
if (r == null) {
//console.log('NOT RIGHT', sendall, firstcode, cdb[2], cdb[3]);
//process.exit(0);
r = Buffer.concat([IntToStr(0x0008), IntToStr(4)]);
} else {
r = Buffer.concat([IntToStr(0x0008), IntToStr(r.length + 4), r]);
}
// Set the length
r = webserver.common.IntToStr(r.length) + r;
// Cut the length to buflen if needed
if (r.length > buflen) { r = r.slice(0, buflen); }
if (r.length > buflen) { r = r.substring(0, buflen); }
obj.SendDataToHost(dev, true, r, featureRegister & 1);
return -1;
case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
//var buflen = (cdb[7] << 8) + cdb[8];
//if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
if (obj.debug) console.log("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb[1], cdb[4], cdb[9]);
if ((cdb[1] != 0x01) && (cdb[4] != 0x10)) {
if (obj.debug) console.log('SCSI ERROR');
//var buflen = (cdb.charCodeAt(7) << 8) + cdb.charCodeAt(8);
//if (buflen == 0) { obj.SendDataToHost(dev, true, webserver.common.IntToStr(0x003c) + webserver.common.IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
debug("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb.charCodeAt(1), cdb.charCodeAt(4), cdb.charCodeAt(9));
if ((cdb.charCodeAt(1) != 0x01) && (cdb.charCodeAt(4) != 0x10)) {
debug('SCSI ERROR');
obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01);
break;
}
var present = 0x00;
if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
obj.SendDataToHost(dev, true, Buffer.from([0x00, present, 0x80, 0x00]), featureRegister & 1); // This is the original version, 4 bytes long
obj.SendDataToHost(dev, true, String.fromCharCode(0x00, present, 0x80, 0x00), featureRegister & 1); // This is the original version, 4 bytes long
break;
case 0x4c:
obj.SendCommand(0x51, Buffer.concat([IntToStrX(0), IntToStrX(0), IntToStrX(0), Buffer.from([0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00])]), true);
obj.SendCommand(0x51, webserver.common.IntToStrX(0) + webserver.common.IntToStrX(0) + webserver.common.IntToStrX(0) + String.fromCharCode(0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00), true);
break;
case 0x51: // READ_DISC_INFO
if (obj.debug) console.log("SCSI READ_DISC_INFO", dev);
debug("SCSI READ_DISC_INFO", dev);
obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
return -1;
case 0x55: // MODE_SELECT_10:
if (obj.debug) console.log("SCSI ERROR: MODE_SELECT_10", dev);
debug("SCSI ERROR: MODE_SELECT_10", dev);
obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
return -1;
case 0x5a: // MODE_SENSE_10
if (obj.debug) console.log("SCSI: MODE_SENSE_10", dev, cdb[2] & 0x3f);
var buflen = ReadShort(cdb, 7);
//var pc = cdb[2] & 0xc0;
debug("SCSI: MODE_SENSE_10", dev, cdb.charCodeAt(2) & 0x3f);
var buflen = webserver.common.ReadShort(cdb, 7);
//var pc = cdb.charCodeAt(2) & 0xc0;
var r = null;
if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
if (buflen == 0) { obj.SendDataToHost(dev, true, webserver.common.IntToStr(0x003c) + webserver.common.IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
// 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
var sectorCount = 0;
if (dev == 0xA0) {
if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
if (obj.floppy != null) { sectorCount = (obj.floppySize >> 9); }
} else {
if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
if (obj.cdrom != null) { sectorCount = (obj.cdromSize >> 11); }
}
switch (cdb[2] & 0x3f) {
case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_FloppyError_Recovery_Array : IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break;
case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_FloppyDisk_Page_Array : IDE_ModeSence_LS120Disk_Page_Array; } break;
case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_3F_Floppy_Array : IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break;
switch (cdb.charCodeAt(2) & 0x3f) {
case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyError_Recovery_Array:IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break;
case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyDisk_Page_Array:IDE_ModeSence_LS120Disk_Page_Array; } break;
case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_3F_Floppy_Array:IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break;
case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
}
@ -581,14 +589,8 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
obj.SendDataToHost(dev, true, r, featureRegister & 1);
}
break;
case 0x51: // READ_DISK_INFORMATION
obj.SendDataToHost(dev, true, RD_CD_DiskInfo, featureRegister & 1);
break;
case 0xAC: // GET_PERFORMANCE
obj.SendDataToHost(dev, true, RD_CD_Performance, featureRegister & 1);
break;
default: // UNKNOWN COMMAND
if (obj.debug) console.log("IDER: Unknown SCSI command", cdb[0]);
debug("IDER: Unknown SCSI command", cdb.charCodeAt(0));
obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
return -1;
}
@ -598,8 +600,8 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
function sendDiskData(dev, lba, len, featureRegister) {
var media = null;
var mediaBlocks = 0;
if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppySize >> 9); } }
if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdromSize >> 11); } }
if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
if (media != null) {
@ -626,10 +628,9 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
g_len -= len;
g_lba += len;
var buffer = Buffer.alloc(len);
fs.read(g_media.ptr, buffer, 0, len, lba, function (error, bytesRead, buffer) {
obj.SendDataToHost(g_dev, (g_len == 0), buffer, featureRegister & 1);
fs.read(g_media, buffer, 0, len, lba, function (error, bytesRead, buffer) {
obj.SendDataToHost(g_dev, (g_len == 0), buffer.toString('binary'), featureRegister & 1);
if ((g_len > 0) && (g_reset == false)) {
sendDiskDataEx(featureRegister);
} else {
@ -642,13 +643,3 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
return obj;
}
function ShortToStr(v) { return Buffer.from([(v >> 8) & 0xFF, v & 0xFF]); }
function ShortToStrX(v) { return Buffer.from([v & 0xFF, (v >> 8) & 0xFF]); }
function IntToStr(v) { return Buffer.from([(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]); }
function IntToStrX(v) { return Buffer.from([v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF]); }
function ReadShort(v, p) { return (v[p] << 8) + v[p + 1]; }
function ReadShortX(v, p) { return (v[p + 1] << 8) + v[p]; }
function ReadInt(v, p) { return (v[p] * 0x1000000) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
function ReadSInt(v, p) { return (v[p] << 24) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; }
function ReadIntX(v, p) { return (v[p + 3] * 0x1000000) + (v[p + 2] << 16) + (v[p + 1] << 8) + v[p]; }

View file

@ -127,8 +127,8 @@ module.exports.CreateAmtIderSession = function (parent, db, ws, req, args, domai
if ((command.args.floppyPath != null) && (typeof command.args.floppyPath != 'string')) { command.args.floppyPath = null; } else { command.args.floppyPath = decodeURIComponent(command.args.floppyPath); }
if ((command.args.cdromPath != null) && (typeof command.args.cdromPath != 'string')) { command.args.cdromPath = null; } else { command.args.cdromPath = decodeURIComponent(command.args.cdromPath); }
// TODO: Double check that "." or ".." are not used.
if ((command.args.floppyPath != null) && (command.args.floppyPath.indexOf('..') >= 0)) { delete command.args.floppyPath; }
if ((command.args.cdromPath != null) && (command.args.cdromPath.indexOf('..') >= 0)) { delete command.args.cdromPath; }
if ((command.args.floppyPath != null) && (command.args.floppyPath.indexOf("..") >= 0)) { delete command.args.floppyPath; }
if ((command.args.cdromPath != null) && (command.args.cdromPath.indexOf("..") >= 0)) { delete command.args.cdromPath; }
// Get the disk image paths
var domainx = 'domain' + ((domain.id == '') ? '' : ('-' + domain.id));
@ -148,7 +148,7 @@ module.exports.CreateAmtIderSession = function (parent, db, ws, req, args, domai
var iderError = obj.ider.m.diskSetup(floppyPath, cdromPath);
// Error with the disk images, unable to start IDER
if (iderError != 0) { try { ws.send(JSON.stringify({ action: 'error', code: iderError })); } catch (ex) { } break; }
if (iderError != 0) { try { ws.send(JSON.stringify({ action: "error", code: iderError })); } catch (ex) { } break; }
// Start the IDER session
obj.ider.Start(req.query.host, req.query.port, req.query.tls);

View file

@ -44,10 +44,10 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.redirTrace = false;
obj.tls1only = 0; // TODO
obj.amtaccumulator = '';
obj.amtaccumulator = "";
obj.amtsequence = 1;
obj.amtkeepalivetimer = null;
obj.authuri = '/RedirectionService';
obj.authuri = "/RedirectionService";
obj.onStateChanged = null;
obj.forwardclient = null;
@ -71,7 +71,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
const SITERIGHT_LOCKED = 32;
function Debug(lvl) {
if ((arguments.length < 2) || (meshcentral.debugLevel == null) || (lvl > meshcentral.debugLevel)) return;
if ((arguments.length < 2) || (lvl > meshcentral.debugLevel)) return;
var a = []; for (var i = 1; i < arguments.length; i++) { a.push(arguments[i]); } console.log(...a);
}
@ -81,7 +81,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
var obj = new require('stream').Duplex(options);
obj.forwardwrite = null;
obj.updateBuffer = function (chunk) { this.push(chunk); };
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._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;
}
@ -478,17 +478,10 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
}
obj.xxSend = function (x) {
if (typeof x == 'string') {
if (obj.redirTrace) { console.log("REDIR-SEND(" + x.length + "): " + Buffer.from(x, 'binary').toString('hex'), typeof x); }
//obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
//obj.forwardclient.write(x); // FIXES CIRA
obj.forwardclient.write(Buffer.from(x, 'binary'));
} else {
if (obj.redirTrace) { console.log("REDIR-SEND(" + x.length + "): " + x.toString('hex'), typeof x); }
//obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
//obj.forwardclient.write(x); // FIXES CIRA
obj.forwardclient.write(x);
}
if (obj.redirTrace) { console.log("REDIR-SEND(" + x.length + "): " + Buffer.from(x, "binary").toString('hex'), typeof x); }
//obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
//obj.forwardclient.write(x); // FIXES CIRA
obj.forwardclient.write(Buffer.from(x, "binary"));
}
obj.Send = function (x) {
@ -504,7 +497,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.xxRandomValueHex = function(len) { return obj.crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }
obj.xxOnSocketClosed = function () {
if (obj.redirTrace) { console.log('REDIR-CLOSED'); }
if (obj.redirTrace) { console.log("REDIR-CLOSED"); }
//obj.Debug("Socket Closed");
obj.Stop();
}
@ -517,12 +510,12 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
}
obj.Stop = function () {
if (obj.redirTrace) { console.log('REDIR-CLOSED'); }
if (obj.redirTrace) { console.log("REDIR-CLOSED"); }
//obj.Debug("Socket Stopped");
obj.xxStateChange(0);
obj.connectstate = -1;
obj.amtaccumulator = '';
if (obj.forwardclient != null) { try { obj.forwardclient.destroy(); } catch (ex) { } delete obj.forwardclient; }
obj.amtaccumulator = "";
if (obj.forwardclient != null) { try { obj.forwardclient.close(); } catch (ex) { } delete obj.forwardclient; }
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); delete obj.amtkeepalivetimer; }
}

View file

@ -170,11 +170,13 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConn
//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; }, {}) }
// NODE.js specific private method
obj.parseDigest = function (header) {
var t = header.substring(7).split(',');
for (var i in t) t[i] = t[i].trim();
return t.reduce(function (obj, s) { var parts = s.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
@ -274,7 +276,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConn
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 };
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, rejectUnauthorized: false };
if (obj.xtlsMethod != 0) { options.secureProtocol = 'TLSv1_method'; }
if (obj.xtlsoptions) {
if (obj.xtlsoptions.ca) { options.ca = obj.xtlsoptions.ca; }

View file

@ -707,15 +707,7 @@ module.exports.CreateAmtManager = function (parent) {
dev.aquired.controlMode = responses['IPS_HostBasedSetupService'].response.CurrentControlMode; // 1 = CCM, 2 = ACM
if (typeof stack.wsman.comm.amtVersion == 'string') { // Set the Intel AMT version using the HTTP header if present
var verSplit = stack.wsman.comm.amtVersion.split('.');
if (verSplit.length >= 2) {
dev.aquired.version = verSplit[0] + '.' + verSplit[1];
dev.aquired.majorver = parseInt(verSplit[0]);
dev.aquired.minorver = parseInt(verSplit[1]);
if (verSplit.length >= 3) {
dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2];
dev.aquired.maintenancever = parseInt(verSplit[2]);
}
}
if (verSplit.length >= 3) { dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2]; dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); }
}
dev.aquired.realm = stack.wsman.comm.digestRealm;
dev.aquired.user = dev.intelamt.user = stack.wsman.comm.user;
@ -758,8 +750,7 @@ module.exports.CreateAmtManager = function (parent) {
// Start power polling if not connected to LMS
var ppfunc = function powerPoleFunction() { fetchPowerState(powerPoleFunction.dev); }
ppfunc.dev = dev;
if(dev.polltimer){ clearInterval(dev.polltimer); delete dev.polltimer; }
dev.polltimer = new setInterval(ppfunc, 290000); // Poll for power state every 4 minutes 50 seconds.
dev.polltimer = new setTimeout(ppfunc, 290000); // Poll for power state every 4 minutes 50 seconds.
fetchPowerState(dev);
} else {
// For LMS connections, close now.
@ -939,8 +930,8 @@ module.exports.CreateAmtManager = function (parent) {
if (response.Body.OSPowerSavingState == 2) { meshPowerState = 1; } // Fully powered (S0);
else if (response.Body.OSPowerSavingState == 3) { meshPowerState = 2; } // Modern standby (We are going to call this S1);
// Set OS power state - connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN
if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), (dev.connType == 3 ? 4 : 2), meshPowerState, null, { name: dev.name }); }
// Set OS power state
if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, meshPowerState, null, { name: dev.name }); }
});
} else {
// Convert the power state
@ -949,83 +940,28 @@ module.exports.CreateAmtManager = function (parent) {
var meshPowerState = -1, powerConversionTable = [-1, -1, 1, 2, 3, 6, 6, 5, 6];
if (powerstate < powerConversionTable.length) { meshPowerState = powerConversionTable[powerstate]; } else { powerstate = 6; }
// Set power state - connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN
if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), (dev.connType == 3 ? 4 : 2), meshPowerState, null, { name: dev.name }); }
// Set power state
if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, meshPowerState, null, { name: dev.name }); }
}
});
}
// Perform a power action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset, 11 = Power on to BIOS, 12 = Reset to BIOS, 13 = Power on to BIOS with SOL, 14 = Reset to BIOS with SOL, 15 = Power on to PXE, 16 = Reset to PXE
// Perform a power action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset
function performPowerAction(nodeid, action) {
console.log('performPowerAction', nodeid, action);
var devices = obj.amtDevices[nodeid];
if (devices == null) return;
for (var i in devices) {
var dev = devices[i];
// If not LMS, has a AMT stack present and is in connected state, perform power operation.
if ((dev.connType != 2) && (dev.state == 1) && (dev.amtstack != null)) {
// Action: 2 = Power on, 8 = Power down, 10 = reset
parent.debug('amt', dev.name, "performPowerAction", action);
dev.powerAction = action;
if (action <= 10) {
// Action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset
try { dev.amtstack.RequestPowerStateChange(action, performPowerActionResponse); } catch (ex) { }
} else {
// 11 = Power on to BIOS, 12 = Reset to BIOS, 13 = Power on to BIOS with SOL, 14 = Reset to BIOS with SOL, 15 = Power on to PXE, 16 = Reset to PXE
dev.amtstack.BatchEnum(null, ['*AMT_BootSettingData'], performAdvancedPowerActionResponse);
}
try { dev.amtstack.RequestPowerStateChange(action, performPowerActionResponse); } catch (ex) { }
}
}
}
// Response to Intel AMT advanced power action
function performAdvancedPowerActionResponse(stack, name, responses, status) {
const dev = stack.dev;
const action = dev.powerAction;
delete dev.powerAction;
if (obj.amtDevices[dev.nodeid] == null) return; // Device no longer exists, ignore this response.
if (status != 200) return;
if ((responses['AMT_BootSettingData'] == null) || (responses['AMT_BootSettingData'].response == null)) return;
var bootSettingData = responses['AMT_BootSettingData'].response;
// Clean up parameters
bootSettingData['ConfigurationDataReset'] = false;
delete bootSettingData['WinREBootEnabled'];
delete bootSettingData['UEFILocalPBABootEnabled'];
delete bootSettingData['UEFIHTTPSBootEnabled'];
delete bootSettingData['SecureBootControlEnabled'];
delete bootSettingData['BootguardStatus'];
delete bootSettingData['OptionsCleared'];
delete bootSettingData['BIOSLastStatus'];
delete bootSettingData['UefiBootParametersArray'];
delete bootSettingData['RPEEnabled'];
delete bootSettingData['RSEPassword']
// Ready boot parameters
bootSettingData['BIOSSetup'] = ((action >= 11) && (action <= 14));
bootSettingData['UseSOL'] = ((action >= 13) && (action <= 14));
if ((action == 11) || (action == 13) || (action == 15)) { dev.powerAction = 2; } // Power on
if ((action == 12) || (action == 14) || (action == 16)) { dev.powerAction = 10; } // Reset
// Set boot parameters
dev.amtstack.Put('AMT_BootSettingData', bootSettingData, function (stack, name, response, status, tag) {
const dev = stack.dev;
if ((obj.amtDevices[dev.nodeid] == null) || (status != 200)) return; // Device no longer exists or error
// Set boot config
dev.amtstack.SetBootConfigRole(1, function (stack, name, response, status, tag) {
const dev = stack.dev;
if ((obj.amtDevices[dev.nodeid] == null) || (status != 200)) return; // Device no longer exists or error
// Set boot order
var bootDevice = (action === 15 || action === 16) ? '<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"><ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootSourceSetting</ResourceURI><SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"><Selector Name="InstanceID">Intel(r) AMT: Force PXE Boot</Selector></SelectorSet></ReferenceParameters>' : null;
dev.amtstack.CIM_BootConfigSetting_ChangeBootOrder(bootDevice, function (stack, name, response, status) {
const dev = stack.dev;
if ((obj.amtDevices[dev.nodeid] == null) || (status != 200)) return; // Device no longer exists or error
// Perform power action
try { dev.amtstack.RequestPowerStateChange(dev.powerAction, performPowerActionResponse); } catch (ex) { }
}, 0, 1);
}, 0, 1);
}, 0, 1);
}
// Response to Intel AMT power action
function performPowerActionResponse(stack, name, responses, status) {
const dev = stack.dev;
@ -1069,7 +1005,7 @@ module.exports.CreateAmtManager = function (parent) {
if (status != 200) { dev.consoleMsg("Failed to get security information (" + status + ")."); delete dev.ocrfile; return; }
// Check if this Intel AMT device supports OCR
if (responses['AMT_BootCapabilities'].response['ForceUEFIHTTPSBoot'] !== true) {
if (responses['AMT_PublicKeyCertificate'].responses['ForceUEFIHTTPSBoot'] !== true) {
dev.consoleMsg("This Intel AMT device does not support UEFI HTTPS boot (" + status + ")."); delete dev.ocrfile; return;
}
@ -1099,14 +1035,11 @@ module.exports.CreateAmtManager = function (parent) {
// Generate the one-time URL.
var cookie = obj.parent.encodeCookie({ a: 'f', f: dev.ocrfile }, obj.parent.loginCookieEncryptionKey)
var url = 'https://' + parent.webserver.certificates.AmtMpsName + ':' + ((parent.args.mpsaliasport != null) ? parent.args.mpsaliasport : parent.args.mpsport) + '/c/' + cookie + '.efi';
var url = 'https://' + parent.webserver.certificates.AmtMpsName + ':' + ((parent.args.mpsaliasport != null) ? parent.args.mpsaliasport : parent.args.mpsport) + '/c/' + cookie + '.iso';
delete dev.ocrfile;
// Generate the boot data for OCR with URL
var r = response.Body;
r['BIOSPause'] = false;
r['BIOSSetup'] = false;
r['EnforceSecureBoot'] = false;
r['UefiBootParametersArray'] = Buffer.from(makeUefiBootParam(1, url) + makeUefiBootParam(20, 1, 1) + makeUefiBootParam(30, 0, 2), 'binary').toString('base64');
r['UefiBootNumberOfParams'] = 3;
r['BootMediaIndex'] = 0; // Do not use boot media index for One Click Recovery (OCR)
@ -1127,7 +1060,8 @@ module.exports.CreateAmtManager = function (parent) {
dev.amtstack.SetBootConfigRole(1, function (stack, name, response, status) {
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
if (status != 200) { dev.consoleMsg("Failed to set boot config role (" + status + ")."); return; }
dev.amtstack.CIM_BootConfigSetting_ChangeBootOrder('<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"><ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootSourceSetting</ResourceURI><SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"><Selector Name="InstanceID">Intel(r) AMT: Force OCR UEFI HTTPS Boot</Selector></SelectorSet></ReferenceParameters>', function (stack, name, response, status) {
var bootSource = 'Force OCR UEFI HTTPS Boot';
dev.amtstack.CIM_BootConfigSetting_ChangeBootOrder((bootSource == null) ? bootSource : '<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"><ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootSourceSetting</ResourceURI><SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"><Selector Name="InstanceID">Intel(r) AMT: ' + bootSource + '</Selector></SelectorSet></ReferenceParameters>', function (stack, name, response, status) {
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
if (status != 200) { dev.consoleMsg("Failed to set boot config (" + status + ")."); return; }
dev.amtstack.RequestPowerStateChange(10, function (stack, name, response, status) { // 10 = Reset, 2 = Power Up
@ -1330,7 +1264,7 @@ module.exports.CreateAmtManager = function (parent) {
}
// Figure out what index is local & remote
var localNdx = ((dev.policy != null) && (dev.policy.tlsSettings != null) && (dev.policy.tlsSettings[0] != null) && (dev.policy.tlsSettings[0]['InstanceID'] == 'Intel(r) AMT LMS TLS Settings')) ? 0 : 1, remoteNdx = (1 - localNdx);
var localNdx = ((dev.policy.tlsSettings[0]['InstanceID'] == 'Intel(r) AMT LMS TLS Settings')) ? 0 : 1, remoteNdx = (1 - localNdx);
// Remote TLS settings
var xxTlsSettings2 = Clone(dev.policy.tlsSettings);
@ -1443,11 +1377,11 @@ module.exports.CreateAmtManager = function (parent) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
const domain = parent.config.domains[dev.domainid];
if ((responses['AMT_PublicKeyCertificate'] == null) || (responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicPrivateKeyPair'] == null) || (responses['AMT_PublicPrivateKeyPair'].status != 200)) { devTaskCompleted(dev); return; } // We can't get the certificate list, fail and carry on.
if ((responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicKeyCertificate'].status != 200)) { devTaskCompleted(dev); return; } // We can't get the certificate list, fail and carry on.
// See if we need to perform wired or wireless 802.1x configuration
var wiredConfig = ((parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) && (responses['AMT_8021XProfile'] != null) && (responses['AMT_8021XProfile'].status == 200));
const wirelessConfig = ((responses['CIM_WiFiEndpointSettings'] != null) && (responses['CIM_WiFiEndpointSettings'].status == 200) && (responses['AMT_WiFiPortConfigurationService'] != null) && (responses['AMT_WiFiPortConfigurationService'].status == 200) && (responses['CIM_WiFiPort'] != null) && (responses['CIM_WiFiPort'].status == 200) && (responses['CIM_IEEE8021xSettings'] != null) && (responses['CIM_IEEE8021xSettings'].status == 200));
var wiredConfig = ((parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) && (responses['AMT_8021XProfile'].status == 200));
const wirelessConfig = ((responses['CIM_WiFiEndpointSettings'].status == 200) && (responses['AMT_WiFiPortConfigurationService'].status == 200) && (responses['CIM_WiFiPort'].status == 200) && (responses['CIM_IEEE8021xSettings'].status == 200));
if (!wiredConfig && !wirelessConfig) { devTaskCompleted(dev); return; } // We can't get wired or wireless settings, ignore and carry on.
// Sort out the certificates
@ -2250,6 +2184,7 @@ module.exports.CreateAmtManager = function (parent) {
dev.amtstack.BatchEnum('', query, attemptSettingsSyncResponse);
}
function attemptSettingsSyncResponse(stack, name, responses, status) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
@ -2304,7 +2239,7 @@ module.exports.CreateAmtManager = function (parent) {
// Check KVM state
if ((dev.aquired.majorver != null) && (dev.aquired.majorver > 5) && (responses['CIM_KVMRedirectionSAP'] != null)) {
const kvm = ((responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 2) || (responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 6));
var kvm = (((responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 6) && (responses['CIM_KVMRedirectionSAP'].response['RequestedState'] == 2)) || (responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 2) || (responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 6));
if (kvm == false) {
// Enable KVM
dev.taskCount++;
@ -2632,14 +2567,7 @@ module.exports.CreateAmtManager = function (parent) {
if (domain && domain.amtmanager && (domain.amtmanager.tlsacmactivation == true)) { TlsAcmActivation = true; }
// Check Intel AMT version
if (typeof dev.intelamt.ver == 'string') {
var verSplit = dev.intelamt.ver.split('.');
if (verSplit.length >= 2) {
dev.aquired.majorver = parseInt(verSplit[0]);
dev.aquired.minorver = parseInt(verSplit[1]);
if (verSplit.length >= 3) { dev.aquired.maintenancever = parseInt(verSplit[2]); }
}
}
if (typeof dev.intelamt.ver == 'string') { var verSplit = dev.intelamt.ver.split('.'); if (verSplit.length >= 3) { dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); } }
// If this is Intel AMT 14 or better and allowed, we are going to attempt a host-based end-to-end TLS activation.
if (TlsAcmActivation && (dev.aquired.majorver >= 14)) {
@ -2695,15 +2623,7 @@ module.exports.CreateAmtManager = function (parent) {
dev.aquired.controlMode = 1; // 1 = CCM, 2 = ACM
if (typeof dev.amtstack.wsman.comm.amtVersion == 'string') {
var verSplit = dev.amtstack.wsman.comm.amtVersion.split('.');
if (verSplit.length >= 2) {
dev.aquired.version = verSplit[0] + '.' + verSplit[1];
dev.aquired.majorver = parseInt(verSplit[0]);
dev.aquired.minorver = parseInt(verSplit[1]);
if (verSplit.length >= 3) {
dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2];
dev.aquired.maintenancever = parseInt(verSplit[2]);
}
}
if (verSplit.length >= 3) { dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2]; dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); }
}
if ((typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) {
dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix;
@ -2838,10 +2758,8 @@ module.exports.CreateAmtManager = function (parent) {
var vs = getInstance(amtlogicalelements, 'AMT')['VersionString'];
if (vs != null) {
dev.aquired.version = vs;
version = dev.aquired.version.split('.')
dev.aquired.versionmajor = parseInt(version[0]);
dev.aquired.versionminor = parseInt(version[1]);
if (version.length > 2) { dev.aquired.versionmaintenance = parseInt(version[2]); }
dev.aquired.versionmajor = parseInt(dev.aquired.version.split('.')[0]);
dev.aquired.versionminor = parseInt(dev.aquired.version.split('.')[1]);
}
}
}
@ -2849,14 +2767,10 @@ module.exports.CreateAmtManager = function (parent) {
// Fetch the Intel AMT version from HTTP stack
if ((dev.amtversionstr == null) && (stack.wsman.comm.amtVersion != null)) {
var s = stack.wsman.comm.amtVersion.split('.');
if (s.length >= 2) {
dev.aquired.version = s[0] + '.' + s[1] + '.';
if (s.length >= 3) {
dev.aquired.version = s[0] + '.' + s[1] + '.' + s[2];
dev.aquired.versionmajor = parseInt(s[0]);
dev.aquired.versionminor = parseInt(s[1]);
if (s.length >= 3) {
dev.aquired.version = s[0] + '.' + s[1] + '.' + s[2];
dev.aquired.versionmaintenance = parseInt(s[2]);
}
}
}

View file

@ -201,10 +201,8 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
var vs = getInstance(amtlogicalelements, 'AMT')['VersionString'];
if (vs != null) {
dev.aquired.version = vs;
const versionSplit = parseInt(dev.aquired.version.split('.'));
dev.aquired.versionmajor = parseInt(versionSplit[0]);
dev.aquired.versionminor = parseInt(versionSplit[1]);
if (versionSplit.length >= 3) { dev.aquired.versionmaintenance = parseInt(versionSplit[2]); }
dev.aquired.versionmajor = parseInt(dev.aquired.version.split('.')[0]);
dev.aquired.versionminor = parseInt(dev.aquired.version.split('.')[1]);
}
}
}
@ -212,14 +210,10 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
// Fetch the Intel AMT version from HTTP stack
if ((dev.amtversionstr == null) && (stack.wsman.comm.amtVersion != null)) {
var s = stack.wsman.comm.amtVersion.split('.');
if (s.length >= 2) {
dev.aquired.version = s[0] + '.' + s[1];
if (s.length >= 3) {
dev.aquired.version = s[0] + '.' + s[1] + '.' + s[2];
dev.aquired.versionmajor = parseInt(s[0]);
dev.aquired.versionminor = parseInt(s[1]);
if (s.length >= 3) {
dev.aquired.version = s[0] + '.' + s[1] + '.' + s[2];
dev.aquired.versionmaintenance = parseInt(s[2]);
}
}
}

View file

@ -362,8 +362,8 @@ module.exports.CreateAmtScanner = function (parent) {
if (oldVer == newVer) return false; // Versions are same already, don't update.
if (newVer == undefined || newVer == null) return false; // New version is bad, don't update it.
if (oldVer == undefined || oldVer == null) return true; // Old version is no good anyway, update it.
var oldVerArr = oldVer.toString().split('.');
var newVerArr = newVer.toString().split('.');
var oldVerArr = oldVer.split('.');
var newVerArr = newVer.split('.');
if ((oldVerArr.length < 2) || (newVerArr.length < 2)) return false;
if ((oldVerArr[0] != newVerArr[0]) || (oldVerArr[1] != newVerArr[1])) return true;
if (newVerArr.length > oldVerArr.length) return true;

View file

@ -1,4 +1,4 @@
/**
/**
* @description MeshCentral MSTSC & SSH relay
* @author Ylian Saint-Hilaire & Bryan Roe
* @copyright Intel Corporation 2018-2022
@ -35,29 +35,28 @@ const PROTOCOL_WEBSFTP = 203;
const PROTOCOL_WEBVNC = 204;
// Mesh Rights
const MESHRIGHT_EDITMESH = 0x00000001; // 1
const MESHRIGHT_MANAGEUSERS = 0x00000002; // 2
const MESHRIGHT_MANAGECOMPUTERS = 0x00000004; // 4
const MESHRIGHT_REMOTECONTROL = 0x00000008; // 8
const MESHRIGHT_AGENTCONSOLE = 0x00000010; // 16
const MESHRIGHT_SERVERFILES = 0x00000020; // 32
const MESHRIGHT_WAKEDEVICE = 0x00000040; // 64
const MESHRIGHT_SETNOTES = 0x00000080; // 128
const MESHRIGHT_REMOTEVIEWONLY = 0x00000100; // 256
const MESHRIGHT_NOTERMINAL = 0x00000200; // 512
const MESHRIGHT_NOFILES = 0x00000400; // 1024
const MESHRIGHT_NOAMT = 0x00000800; // 2048
const MESHRIGHT_DESKLIMITEDINPUT = 0x00001000; // 4096
const MESHRIGHT_LIMITEVENTS = 0x00002000; // 8192
const MESHRIGHT_CHATNOTIFY = 0x00004000; // 16384
const MESHRIGHT_UNINSTALL = 0x00008000; // 32768
const MESHRIGHT_NODESKTOP = 0x00010000; // 65536
const MESHRIGHT_REMOTECOMMAND = 0x00020000; // 131072
const MESHRIGHT_RESETOFF = 0x00040000; // 262144
const MESHRIGHT_GUESTSHARING = 0x00080000; // 524288
const MESHRIGHT_DEVICEDETAILS = 0x00100000; // 1048576
const MESHRIGHT_RELAY = 0x00200000; // 2097152
const MESHRIGHT_ADMIN = 0xFFFFFFFF;
const MESHRIGHT_EDITMESH = 0x00000001; // 1
const MESHRIGHT_MANAGEUSERS = 0x00000002; // 2
const MESHRIGHT_MANAGECOMPUTERS = 0x00000004; // 4
const MESHRIGHT_REMOTECONTROL = 0x00000008; // 8
const MESHRIGHT_AGENTCONSOLE = 0x00000010; // 16
const MESHRIGHT_SERVERFILES = 0x00000020; // 32
const MESHRIGHT_WAKEDEVICE = 0x00000040; // 64
const MESHRIGHT_SETNOTES = 0x00000080; // 128
const MESHRIGHT_REMOTEVIEWONLY = 0x00000100; // 256
const MESHRIGHT_NOTERMINAL = 0x00000200; // 512
const MESHRIGHT_NOFILES = 0x00000400; // 1024
const MESHRIGHT_NOAMT = 0x00000800; // 2048
const MESHRIGHT_DESKLIMITEDINPUT = 0x00001000; // 4096
const MESHRIGHT_LIMITEVENTS = 0x00002000; // 8192
const MESHRIGHT_CHATNOTIFY = 0x00004000; // 16384
const MESHRIGHT_UNINSTALL = 0x00008000; // 32768
const MESHRIGHT_NODESKTOP = 0x00010000; // 65536
const MESHRIGHT_REMOTECOMMAND = 0x00020000; // 131072
const MESHRIGHT_RESETOFF = 0x00040000; // 262144
const MESHRIGHT_GUESTSHARING = 0x00080000; // 524288
const MESHRIGHT_DEVICEDETAILS = 0x00100000; // 1048576
const MESHRIGHT_ADMIN = 0xFFFFFFFF;
// SerialTunnel object is used to embed TLS within another connection.
function SerialTunnel(options) {
@ -70,7 +69,7 @@ function SerialTunnel(options) {
}
// Construct a Web relay object
module.exports.CreateWebRelaySession = function (parent, db, req, args, domain, userid, nodeid, addr, port, appid, sessionid, expire, mtype) {
module.exports.CreateWebRelaySession = function (parent, db, req, args, domain, userid, nodeid, addr, port, appid) {
const obj = {};
obj.parent = parent;
obj.lastOperation = Date.now();
@ -80,27 +79,14 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
obj.addr = addr;
obj.port = port;
obj.appid = appid;
obj.sessionid = sessionid;
obj.expireTimer = null;
obj.mtype = mtype;
var pendingRequests = [];
var nextTunnelId = 1;
var tunnels = {};
var errorCount = 0; // If we keep closing tunnels without processing requests, fail the requests
parent.parent.debug('webrelay', 'CreateWebRelaySession, userid:' + userid + ', addr:' + addr + ', port:' + port);
// Any HTTP cookie set by the device is going to be shared between all tunnels to that device.
obj.webCookies = {};
// Setup an expire time if needed
if (expire != null) {
var timeout = (expire - Date.now());
if (timeout < 10) { timeout = 10; }
parent.parent.debug('webrelay', 'timeout set to ' + Math.floor(timeout / 1000) + ' second(s).');
obj.expireTimer = setTimeout(function () { parent.parent.debug('webrelay', 'timeout'); close(); }, timeout);
}
// Events
obj.closed = false;
obj.onclose = null;
@ -124,22 +110,18 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
// Handle new HTTP request
obj.handleRequest = function (req, res) {
parent.parent.debug('webrelay', 'handleRequest, url:' + req.url);
pendingRequests.push([req, res, false]);
handleNextRequest();
}
// Handle new websocket request
obj.handleWebSocket = function (ws, req) {
parent.parent.debug('webrelay', 'handleWebSocket, url:' + req.url);
pendingRequests.push([req, ws, true]);
handleNextRequest();
}
// Handle request
function handleNextRequest() {
if (obj.closed == true) return;
// if there are not pending requests, do nothing
if (pendingRequests.length == 0) return;
@ -149,77 +131,51 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
// Check to see if any of the tunnels are free
var count = 0;
for (var i in tunnels) {
count += ((tunnels[i].isWebSocket || tunnels[i].isStreaming) ? 0 : 1);
if ((tunnels[i].relayActive == true) && (tunnels[i].res == null) && (tunnels[i].isWebSocket == false) && (tunnels[i].isStreaming == false)) {
count += (tunnels[i].isWebSocket ? 0 : 1);
if ((tunnels[i].relayActive == true) && (tunnels[i].res == null) && (tunnels[i].isWebSocket == false)) {
// Found a free tunnel, use it
const x = pendingRequests.shift();
if (x[2] == true) { tunnels[i].processWebSocket(x[0], x[1]); } else { tunnels[i].processRequest(x[0], x[1]); }
return;
}
}
if (count > 0) return;
launchNewTunnel();
}
function launchNewTunnel() {
// Launch a new tunnel
if (obj.closed == true) return;
parent.parent.debug('webrelay', 'launchNewTunnel');
const tunnel = module.exports.CreateWebRelay(obj, db, args, domain, obj.mtype);
const tunnel = module.exports.CreateWebRelay(obj, db, args, domain);
tunnel.onclose = function (tunnelId, processedCount) {
if (tunnels == null) return;
parent.parent.debug('webrelay', 'tunnel-onclose');
if (processedCount == 0) { errorCount++; } // If this tunnel closed without processing any requests, mark this as an error
delete tunnels[tunnelId];
handleNextRequest();
}
tunnel.onconnect = function (tunnelId) {
if (tunnels == null) return;
parent.parent.debug('webrelay', 'tunnel-onconnect');
if (pendingRequests.length > 0) {
const x = pendingRequests.shift();
if (x[2] == true) { tunnels[tunnelId].processWebSocket(x[0], x[1]); } else { tunnels[tunnelId].processRequest(x[0], x[1]); }
}
}
tunnel.oncompleted = function (tunnelId, closed) {
if (tunnels == null) return;
if (closed === true) {
parent.parent.debug('webrelay', 'tunnel-oncompleted and closed');
} else {
parent.parent.debug('webrelay', 'tunnel-oncompleted');
tunnel.oncompleted = function (tunnelId) {
errorCount = 0; // Something got completed, clear any error count
if (pendingRequests.length > 0) {
const x = pendingRequests.shift();
if (x[2] == true) { tunnels[tunnelId].processWebSocket(x[0], x[1]); } else { tunnels[tunnelId].processRequest(x[0], x[1]); }
}
if (closed !== true) {
errorCount = 0; // Something got completed, clear any error count
if (pendingRequests.length > 0) {
const x = pendingRequests.shift();
if (x[2] == true) { tunnels[tunnelId].processWebSocket(x[0], x[1]); } else { tunnels[tunnelId].processRequest(x[0], x[1]); }
}
}
}
tunnel.onNextRequest = function () {
if (tunnels == null) return;
parent.parent.debug('webrelay', 'tunnel-onNextRequest');
handleNextRequest();
}
tunnel.connect(userid, nodeid, addr, port, appid);
tunnel.tunnelId = nextTunnelId++;
tunnels[tunnel.tunnelId] = tunnel;
}
// Close all tunnels
obj.close = function () { close(); }
// Close all tunnels
function close() {
// Set the session as closed
if (obj.closed == true) return;
parent.parent.debug('webrelay', 'tunnel-close');
obj.closed = true;
// Clear the time if present
if (obj.expireTimer != null) { clearTimeout(obj.expireTimer); delete obj.expireTimer; }
// Close all tunnels
for (var i in tunnels) { tunnels[i].close(); }
tunnels = null;
@ -228,7 +184,7 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
for (var i in pendingRequests) { if (pendingRequests[i][2] == true) { pendingRequests[i][1].close(); } else { pendingRequests[i][1].end(); } }
// Notify of session closure
if (obj.onclose) { obj.onclose(obj.sessionid); }
if (obj.onclose) { obj.onclose(obj.userid + '/' + obj.sessionId); }
// Cleanup
delete obj.userid;
@ -240,7 +196,7 @@ module.exports.CreateWebRelaySession = function (parent, db, req, args, domain,
// Construct a Web relay object
module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
module.exports.CreateWebRelay = function (parent, db, args, domain) {
//const Net = require('net');
const WebSocket = require('ws')
@ -248,49 +204,26 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
obj.lastOperation = Date.now();
obj.relayActive = false;
obj.closed = false;
obj.isWebSocket = false; // If true, this request will not close and so, it can't be allowed to hold up other requests
obj.isStreaming = false; // If true, this request will not close and so, it can't be allowed to hold up other requests
obj.isWebSocket = false;
obj.processedRequestCount = 0;
obj.mtype = mtype;
const constants = (require('crypto').constants ? require('crypto').constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
// Events
obj.onclose = null;
obj.oncompleted = null;
obj.onconnect = null;
obj.onNextRequest = null;
// Called when we need to close the tunnel because the response stream has closed
function handleResponseClosure() { obj.close(); }
// Return cookie name and values
function parseRequestCookies(cookiesString) {
var r = {};
if (typeof cookiesString != 'string') return r;
var cookieString = cookiesString.split('; ');
for (var i in cookieString) { var j = cookieString[i].indexOf('='); if (j > 0) { r[cookieString[i].substring(0, j)] = cookieString[i].substring(j + 1); } }
return r;
}
// Process a HTTP request
obj.processRequest = function (req, res) {
if (obj.relayActive == false) { console.log("ERROR: Attempt to use an unconnected tunnel"); return false; }
parent.lastOperation = obj.lastOperation = Date.now();
// Check if this is a websocket
if (req.headers['upgrade'] == 'websocket') { console.log('Attempt to process a websocket in HTTP tunnel method.'); res.end(); return false; }
// If the response stream is closed, close this tunnel right away
res.socket.on('end', handleResponseClosure);
// Construct the HTTP request
var request = req.method + ' ' + req.url + ' HTTP/' + req.httpVersion + '\r\n';
const blockedHeaders = ['cookie', 'upgrade-insecure-requests', 'sec-ch-ua', 'sec-ch-ua-mobile', 'dnt', 'sec-fetch-user', 'sec-ch-ua-platform', 'sec-fetch-site', 'sec-fetch-mode', 'sec-fetch-dest']; // These are headers we do not forward
const blockedHeaders = ['origin', 'cookie']; // These are headers we do not forward
for (var i in req.headers) { if (blockedHeaders.indexOf(i) == -1) { request += i + ': ' + req.headers[i] + '\r\n'; } }
var cookieStr = '';
for (var i in parent.webCookies) { if (cookieStr != '') { cookieStr += '; ' } cookieStr += (i + '=' + parent.webCookies[i].value); }
var reqCookies = parseRequestCookies(req.headers.cookie);
for (var i in reqCookies) { if ((i != 'xid') && (i != 'xid.sig')) { if (cookieStr != '') { cookieStr += '; ' } cookieStr += (i + '=' + reqCookies[i]); } }
if (cookieStr.length > 0) { request += 'cookie: ' + cookieStr + '\r\n' } // If we have session cookies, set them in the header here
request += '\r\n';
@ -324,9 +257,6 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
// Pause the websocket until we get a tunnel connected
obj.ws._socket.pause();
// If the response stream is closed, close this tunnel right away
obj.ws._socket.on('end', function () { obj.close(); });
// Remove the trailing '/.websocket' if needed
var baseurl = req.url, i = req.url.indexOf('?');
if (i > 0) { baseurl = req.url.substring(0, i); }
@ -334,7 +264,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
// Construct the HTTP request
var request = req.method + ' ' + req.url + ' HTTP/' + req.httpVersion + '\r\n';
const blockedHeaders = ['cookie', 'sec-websocket-extensions']; // These are headers we do not forward
const blockedHeaders = ['origin', 'cookie', 'sec-websocket-extensions']; // These are headers we do not forward
for (var i in req.headers) { if (blockedHeaders.indexOf(i) == -1) { request += i + ': ' + req.headers[i] + '\r\n'; } }
var cookieStr = '';
for (var i in parent.webCookies) { if (cookieStr != '') { cookieStr += '; ' } cookieStr += (i + '=' + parent.webCookies[i].value); }
@ -358,7 +288,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
function sendWebSocketFrameToDevice(op, payload) {
// Select a random mask
const mask = parent.parent.parent.crypto.randomBytes(4)
const mask = parent.parent.crypto.randomBytes(4)
// Setup header and mask
var header = null;
@ -394,13 +324,6 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
if (obj.closed == true) return;
obj.closed = true;
// If we are processing a http response that terminates when it closes, do this now.
if ((obj.socketParseState == 1) && (obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'close')) {
processHttpResponse(null, obj.socketAccumulator, true, true); // Indicate this tunnel is done and also closed, do not put a new request on this tunnel.
obj.socketAccumulator = '';
obj.socketParseState = 0;
}
if (obj.tls) {
try { obj.tls.end(); } catch (ex) { console.log(ex); }
delete obj.tls;
@ -431,7 +354,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
}
// Close any pending request
if (obj.res) { obj.res.socket.removeListener('end', handleResponseClosure); obj.res.end(); delete obj.res; }
if (obj.res) { obj.res.end(); delete obj.res; }
if (obj.ws) { obj.ws.close(); delete obj.ws; }
// Event disconnection
@ -440,7 +363,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
obj.relayActive = false;
};
// Start the loopback server
// Start the looppback server
obj.connect = function (userid, nodeid, addr, port, appid) {
if (obj.relayActive || obj.closed) return;
obj.addr = addr;
@ -450,7 +373,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
// Encode a cookie for the mesh relay
const cookieContent = { userid: userid, domainid: domain.id, nodeid: nodeid, tcpport: port };
if (addr != null) { cookieContent.tcpaddr = addr; }
const cookie = parent.parent.parent.encodeCookie(cookieContent, parent.parent.parent.loginCookieEncryptionKey);
const cookie = parent.parent.encodeCookie(cookieContent, parent.parent.loginCookieEncryptionKey);
try {
// Setup the correct URL with domain and use TLS only if needed.
@ -458,11 +381,10 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
const protocol = (args.tlsoffload) ? 'ws' : 'wss';
var domainadd = '';
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
var url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=14&auth=' + cookie; // Protocol 14 is Web-TCP
if (domain.id != '') { url += '&domainid=' + domain.id; } // Since we are using "localhost", we are going to signal what domain we are on using a URL argument.
parent.parent.parent.debug('relay', 'TCP: Connection websocket to ' + url);
const url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=14&auth=' + cookie; // Protocol 14 is Web-TCP
parent.parent.debug('relay', 'TCP: Connection websocket to ' + url);
obj.wsClient = new WebSocket(url, options);
obj.wsClient.on('open', function () { parent.parent.parent.debug('relay', 'TCP: Relay websocket open'); });
obj.wsClient.on('open', function () { parent.parent.debug('relay', 'TCP: Relay websocket open'); });
obj.wsClient.on('message', function (data) { // Make sure to handle flow control.
if (obj.tls) {
// WS --> TLS
@ -477,13 +399,13 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel
const tlsoptions = { socket: obj.ser, rejectUnauthorized: false };
obj.tls = require('tls').connect(tlsoptions, function () {
parent.parent.parent.debug('relay', "Web Relay Secure TLS Connection");
parent.parent.debug('relay', "Web Relay Secure TLS Connection");
obj.relayActive = true;
parent.lastOperation = obj.lastOperation = Date.now(); // Update time of last opertion performed
if (obj.onconnect) { obj.onconnect(obj.tunnelId); } // Event connection
});
obj.tls.setEncoding('binary');
obj.tls.on('error', function (err) { parent.parent.parent.debug('relay', "Web Relay TLS Connection Error", err); obj.close(); });
obj.tls.on('error', function (err) { parent.parent.debug('relay', "Web Relay TLS Connection Error", err); obj.close(); });
// Decrypted tunnel from TLS communcation to be forwarded to the browser
obj.tls.on('data', function (data) { processHttpData(data); }); // TLS ---> Browser
@ -498,8 +420,8 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
processRawHttpData(data);
}
});
obj.wsClient.on('close', function () { parent.parent.parent.debug('relay', 'TCP: Relay websocket closed'); obj.close(); });
obj.wsClient.on('error', function (err) { parent.parent.parent.debug('relay', 'TCP: Relay websocket error: ' + err); obj.close(); });
obj.wsClient.on('close', function () { parent.parent.debug('relay', 'TCP: Relay websocket closed'); obj.close(); });
obj.wsClient.on('error', function (err) { parent.parent.debug('relay', 'TCP: Relay websocket error: ' + err); obj.close(); });
} catch (ex) {
console.log(ex);
}
@ -527,7 +449,6 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
obj.socketParseState = 0;
obj.socketContentLengthRemaining = 0;
function processHttpData(data) {
//console.log('processHttpData', data.length);
obj.socketAccumulator += data;
while (true) {
//console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator);
@ -551,15 +472,9 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
}
}
// Check if this is a streaming response
if ((obj.socketXHeader['content-type'] != null) && (obj.socketXHeader['content-type'].toLowerCase().indexOf('text/event-stream') >= 0)) {
obj.isStreaming = true; // This tunnel is now a streaming tunnel and will not close anytime soon.
if (obj.onNextRequest != null) obj.onNextRequest(); // Call this so that any HTTP requests that are waitting for this one to finish get handled by a new tunnel.
}
// Check if this HTTP request has a body
if (obj.socketXHeader['content-length'] != null) { obj.socketParseState = 1; }
if ((obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'close')) { obj.socketParseState = 1; }
if (obj.socketXHeader['content-length'] != null) { obj.socketParseState = 1; }
if ((obj.socketXHeader['transfer-encoding'] != null) && (obj.socketXHeader['transfer-encoding'].toLowerCase() == 'chunked')) { obj.socketParseState = 1; }
if (obj.isWebSocket) {
if ((obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'upgrade')) {
@ -576,35 +491,21 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
}
if (obj.socketParseState == 1) {
var csize = -1;
if (obj.socketXHeader['content-length'] != null) {
if ((obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'close')) {
// The body ends with a close, in this case, we will only process the header
processHttpResponse(null, null, true);
csize = 0;
} else if (obj.socketXHeader['content-length'] != null) {
// The body length is specified by the content-length
if (obj.socketContentLengthRemaining == 0) { obj.socketContentLengthRemaining = parseInt(obj.socketXHeader['content-length']); } // Set the remaining content-length if not set
var data = obj.socketAccumulator.substring(0, obj.socketContentLengthRemaining); // Grab the available data, not passed the expected content-length
obj.socketAccumulator = obj.socketAccumulator.substring(data.length); // Remove the data from the accumulator
obj.socketContentLengthRemaining -= data.length; // Substract the obtained data from the expected size
if (obj.socketContentLengthRemaining > 0) {
// Send any data we have, if we are done, signal the end of the response
processHttpResponse(null, data, false);
return; // More data is needed, return now so we exit the while() loop.
} else {
// We are done with this request
const closing = (obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'close');
if (closing) {
// We need to close this tunnel.
processHttpResponse(null, data, false);
obj.close();
} else {
// Proceed with the next request.
processHttpResponse(null, data, true);
}
}
processHttpResponse(null, data, (obj.socketContentLengthRemaining == 0)); // Send any data we have, if we are done, signal the end of the response
if (obj.socketContentLengthRemaining > 0) return; // If more data is needed, return now so we exit the while() loop.
csize = 0; // We are done
} else if ((obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'close')) {
// The body ends with a close, in this case, we will only process the header
processHttpResponse(null, obj.socketAccumulator, false);
obj.socketAccumulator = '';
return;
} else if ((obj.socketXHeader['transfer-encoding'] != null) && (obj.socketXHeader['transfer-encoding'].toLowerCase() == 'chunked')) {
}
else if ((obj.socketXHeader['transfer-encoding'] != null) && (obj.socketXHeader['transfer-encoding'].toLowerCase() == 'chunked')) {
// 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.
@ -682,17 +583,16 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
}
// This is a fully parsed HTTP response from the remote device
function processHttpResponse(header, data, done, closed) {
//console.log('processHttpResponse', header, data ? data.length : 0, done, closed);
function processHttpResponse(header, data, done) {
//console.log('processHttpResponse');
if (obj.isWebSocket == false) {
if (obj.res == null) return;
parent.lastOperation = obj.lastOperation = Date.now(); // Update time of last opertion performed
// If there is a header, send it
if (header != null) {
const statusCode = parseInt(header.Directive[1]);
if ((!isNaN(statusCode)) && (statusCode > 0) && (statusCode <= 999)) { obj.res.status(statusCode); } // Set the status
const blockHeaders = ['Directive', 'sec-websocket-extensions', 'connection', 'transfer-encoding', 'last-modified', 'content-security-policy', 'cache-control']; // We do not forward these headers
obj.res.status(parseInt(header.Directive[1])); // Set the status
const blockHeaders = ['Directive', 'sec-websocket-extensions']; // We do not forward these headers
for (var i in header) {
if (i == 'set-cookie') {
for (var ii in header[i]) {
@ -717,38 +617,33 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
}
}
}
else if (blockHeaders.indexOf(i) == -1) { obj.res.set(i.trim(), header[i]); } // Set the headers if not blocked
else if (blockHeaders.indexOf(i) == -1) { obj.res.set(i, header[i]); } // Set the headers if not blocked
}
// Dont set any Content-Security-Policy at all because some applications like Node-Red, access external websites from there javascript which would be forbidden by the below CSP
//obj.res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;"); // Set an "allow all" policy, see if the can restrict this in the future
//obj.res.set('Content-Security-Policy', "default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';"); // Set an "allow all" policy, see if the can restrict this in the future
obj.res.set('Cache-Control', 'no-store'); // Tell the browser not to cache the responses since since the relay port can be used for many relays
obj.res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;"); // Set an "allow all" policy, see if the can restrict this in the future
obj.res.set('Cache-Control', 'no-cache'); // Tell the browser not to cache the responses since since the relay port can be used for many relays
}
// If there is data, send it
if (data != null) { try { obj.res.write(data, 'binary'); } catch (ex) { } }
if (data != null) { obj.res.write(data, 'binary'); }
// If we are done, close the response
if (done == true) {
// Close the response
obj.res.socket.removeListener('end', handleResponseClosure);
obj.res.end();
delete obj.res;
// Event completion
obj.processedRequestCount++;
if (obj.oncompleted) { obj.oncompleted(obj.tunnelId, closed); }
if (obj.oncompleted) { obj.oncompleted(obj.tunnelId); }
}
} else {
// Tunnel is now in web socket pass-thru mode
if (header != null) {
if ((typeof header.connection == 'string') && (header.connection.toLowerCase() == 'upgrade')) {
// Websocket upgrade succesful
obj.socketParseState = 2;
} else {
// Unable to upgrade to web socket
obj.close();
}
if ((typeof header.connection == 'string') && (header.connection.toLowerCase() == 'upgrade')) {
// Websocket upgrade succesful
obj.socketParseState = 2;
} else {
// Unable to upgrade to web socket
obj.close();
}
}
}
@ -756,7 +651,7 @@ module.exports.CreateWebRelay = function (parent, db, args, domain, mtype) {
// Send data thru the relay tunnel. Written to use TLS if needed.
function send(data) { try { if (obj.tls) { obj.tls.write(data); } else { obj.wsClient.send(data); } } catch (ex) { } }
parent.parent.parent.debug('relay', 'TCP: Request for web relay');
parent.parent.debug('relay', 'TCP: Request for web relay');
return obj;
};
@ -821,7 +716,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
obj.relaySocket = socket;
obj.relaySocket.pause();
obj.relaySocket.on('data', function (chunk) { // Make sure to handle flow control.
if (obj.relayActive == true) { obj.relaySocket.pause(); if (obj.wsClient != null) { obj.wsClient.send(chunk, function () { obj.relaySocket.resume(); }); } }
if (obj.relayActive == true) { obj.relaySocket.pause(); obj.wsClient.send(chunk, function () { obj.relaySocket.resume(); }); }
});
obj.relaySocket.on('end', function () { obj.close(); });
obj.relaySocket.on('error', function (err) { obj.close(); });
@ -831,8 +726,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
const protocol = (args.tlsoffload) ? 'ws' : 'wss';
var domainadd = '';
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
var url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=10&auth=' + obj.infos.ip; // Protocol 10 is Web-RDP
if (domain.id != '') { url += '&domainid=' + domain.id; } // Since we are using "localhost", we are going to signal what domain we are on using a URL argument.
const url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=10&auth=' + obj.infos.ip; // Protocol 10 is Web-RDP
parent.parent.debug('relay', 'RDP: Connection websocket to ' + url);
obj.wsClient = new WebSocket(url, options);
obj.wsClient.on('open', function () { parent.parent.debug('relay', 'RDP: Relay websocket open'); });
@ -843,21 +737,22 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
obj.relaySocket.resume();
}
} else {
try { // Forward any ping/pong commands to the browser
var cmd = JSON.parse(data);
if (typeof data == 'string') {
// Forward any ping/pong commands to the browser
var cmd = null;
try { cmd = JSON.parse(data); } catch (ex) { }
if ((cmd != null) && (cmd.ctrlChannel == '102938')) {
if (cmd.type == 'ping') { send(['ping']); }
else if (cmd.type == 'pong') { send(['pong']); }
}
return;
} catch (ex) { // You are not JSON data so just send over relaySocket
obj.wsClient._socket.pause();
try {
obj.relaySocket.write(data, function () {
if (obj.wsClient && obj.wsClient._socket) { try { obj.wsClient._socket.resume(); } catch (ex) { console.log(ex); } }
});
} catch (ex) { console.log(ex); obj.close(); }
}
obj.wsClient._socket.pause();
try {
obj.relaySocket.write(data, function () {
if (obj.wsClient && obj.wsClient._socket) { try { obj.wsClient._socket.resume(); } catch (ex) { console.log(ex); } }
});
} catch (ex) { console.log(ex); obj.close(); }
}
});
obj.wsClient.on('close', function () { parent.parent.debug('relay', 'RDP: Relay websocket closed'); obj.close(); });
@ -890,7 +785,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
rdpClient = require('./rdp').createClient(args).on('connect', function () {
send(['rdp-connect']);
if ((typeof obj.infos.options == 'object') && (obj.infos.options.savepass == true)) { saveRdpCredentials(); } // Save the credentials if needed
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
obj.startTime = Date.now();
// Event session start
@ -984,7 +879,6 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
if ((node == null) || (visible == false) || ((rights & MESHRIGHT_REMOTECONTROL) == 0)) { obj.close(); return; }
if ((rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_REMOTEVIEWONLY) != 0)) { obj.viewonly = true; }
if ((rights != MESHRIGHT_ADMIN) && ((rights & MESHRIGHT_DESKLIMITEDINPUT) != 0)) { obj.limitedinput = true; }
node = parent.common.unEscapeLinksFieldName(node); // unEscape node data for rdp/ssh credentials
obj.mtype = node.mtype; // Store the device group type
obj.meshid = node.meshid; // Store the MeshID
@ -1047,11 +941,7 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
if ((k == 14) || (k == 28)) { ok = true; } // Enter and backspace
if (ok == false) return;
}
var extended = false;
var extendedkeys = [57419,57421,57416,57424,57426,57427,57417,57425,57372,57397,57415,57423,57373,57400,57399];
// left,right,up,down,insert,delete,pageup,pagedown,numpadenter,numpaddivide,home,end,controlright,altright,printscreen
if (extendedkeys.includes(msg[1])) extended=true;
if (rdpClient && (obj.viewonly != true)) { rdpClient.sendKeyEventScancode(msg[1], msg[2], extended); } break;
if (rdpClient && (obj.viewonly != true)) { rdpClient.sendKeyEventScancode(msg[1], msg[2]); } break;
}
case 'unicode': { if (rdpClient && (obj.viewonly != true)) { rdpClient.sendKeyEventUnicode(msg[1], msg[2]); } break; }
case 'utype': {
@ -1215,8 +1105,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
const protocol = (args.tlsoffload) ? 'ws' : 'wss';
var domainadd = '';
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
var url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=11&auth=' + obj.xcookie; // Protocol 11 is Web-SSH
if (domain.id != '') { url += '&domainid=' + domain.id; } // Since we are using "localhost", we are going to signal what domain we are on using a URL argument.
const url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=11&auth=' + obj.xcookie; // Protocol 11 is Web-SSH
parent.parent.debug('relay', 'SSH: Connection websocket to ' + url);
obj.wsClient = new WebSocket(url, options);
obj.wsClient.on('open', function () { parent.parent.debug('relay', 'SSH: Relay websocket open'); });
@ -1232,7 +1121,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
obj.sshClient.on('ready', function () { // Authentication was successful.
// If requested, save the credentials
saveSshCredentials(obj.keep);
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
obj.startTime = Date.now();
// Event start of session
@ -1282,14 +1171,16 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
ws._socket.resume();
}
} else {
try { // Forward any ping/pong commands to the browser
if (typeof data == 'string') {
// Forward any ping/pong commands to the browser
var cmd = null;
cmd = JSON.parse(data);
try { cmd = JSON.parse(data); } catch (ex) { }
if ((cmd != null) && (cmd.ctrlChannel == '102938') && ((cmd.type == 'ping') || (cmd.type == 'pong'))) { obj.ws.send(data); }
return;
} catch(ex) { // Relay WS --> SSH instead
if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
}
// Relay WS --> SSH
if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
}
});
obj.wsClient.on('close', function () { parent.parent.debug('relay', 'SSH: Relay websocket closed'); obj.close(); });
@ -1317,7 +1208,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
// Check if we have SSH credentials for this device
parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
const node = parent.common.unEscapeLinksFieldName(nodes[0]); // unEscape node data for rdp/ssh credentials
const node = nodes[0];
if ((domain.allowsavingdevicecredentials === false) || (node.ssh == null) || (typeof node.ssh != 'object') || (node.ssh[obj.userid] == null) || (typeof node.ssh[obj.userid].u != 'string') || ((typeof node.ssh[obj.userid].p != 'string') && (typeof node.ssh[obj.userid].k != 'string'))) {
// Send a request for SSH authentication
try { ws.send(JSON.stringify({ action: 'sshauth' })) } catch (ex) { }
@ -1365,7 +1256,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
obj.termSize = msg;
parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
if ((err != null) || (nodes == null) || (nodes.length != 1)) return;
const node = parent.common.unEscapeLinksFieldName(nodes[0]); // unEscape node data for rdp/ssh credentials
const node = nodes[0];
if (node.ssh != null) {
obj.username = node.ssh.u;
obj.privateKey = node.ssh.k;
@ -1407,7 +1298,7 @@ module.exports.CreateSshRelay = function (parent, db, ws, req, args, domain) {
parent.parent.db.Get(obj.cookie.nodeid, function (err, nodes) {
if (obj.cookie == null) return; // obj has been cleaned up, just exit.
if ((err != null) || (nodes == null) || (nodes.length != 1)) { parent.parent.debug('relay', 'SSH: Invalid device'); obj.close(); }
const node = parent.common.unEscapeLinksFieldName(nodes[0]); // unEscape node data for rdp/ssh credentials
const node = nodes[0];
obj.nodeid = node._id; // Store the NodeID
obj.meshid = node.meshid; // Store the MeshID
obj.mtype = node.mtype; // Store the device group type
@ -1552,8 +1443,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
const protocol = (args.tlsoffload) ? 'ws' : 'wss';
var domainadd = '';
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
var url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=11&auth=' + authCookie // Protocol 11 is Web-SSH
if (domain.id != '') { url += '&domainid=' + domain.id; } // Since we are using "localhost", we are going to signal what domain we are on using a URL argument.
const url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=11&auth=' + authCookie // Protocol 11 is Web-SSH
parent.parent.debug('relay', 'SSH: Connection websocket to ' + url);
obj.wsClient = new WebSocket(url, options);
obj.wsClient.on('open', function () { parent.parent.debug('relay', 'SSH: Relay websocket open'); });
@ -1569,7 +1459,7 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
obj.sshClient.on('ready', function () { // Authentication was successful.
// If requested, save the credentials
saveSshCredentials(obj.keep);
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
obj.startTime = Date.now();
try {
@ -1620,14 +1510,16 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
ws._socket.resume();
}
} else {
try { // Forward any ping/pong commands to the browser
if (typeof data == 'string') {
// Forward any ping/pong commands to the browser
var cmd = null;
cmd = JSON.parse(data);
try { cmd = JSON.parse(data); } catch (ex) { }
if ((cmd != null) && (cmd.ctrlChannel == '102938') && ((cmd.type == 'ping') || (cmd.type == 'pong'))) { try { obj.ws.send(data); } catch (ex) { console.log(ex); } }
return;
} catch (ex) { // Relay WS --> SSH
if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
}
// Relay WS --> SSH
if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
}
});
obj.wsClient.on('close', function () {
@ -1740,7 +1632,6 @@ module.exports.CreateSshTerminalRelay = function (parent, db, ws, req, domain, u
if ((user == null) || (req.query.nodeid == null)) { obj.close(); return; } // Invalid nodeid
parent.GetNodeWithRights(domain, user, req.query.nodeid, function (node, rights, visible) {
if (obj.ws == null) return; // obj has been cleaned up, just exit.
node = parent.common.unEscapeLinksFieldName(node); // unEscape node data for rdp/ssh credentials
// Check permissions
if ((rights & 8) == 0) { obj.close(); return; } // No MESHRIGHT_REMOTECONTROL rights
@ -1905,8 +1796,7 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
const protocol = (args.tlsoffload) ? 'ws' : 'wss';
var domainadd = '';
if ((domain.dns == null) && (domain.id != '')) { domainadd = domain.id + '/' }
var url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=13&auth=' + authCookie // Protocol 13 is Web-SSH-Files
if (domain.id != '') { url += '&domainid=' + domain.id; } // Since we are using "localhost", we are going to signal what domain we are on using a URL argument.
const url = protocol + '://localhost:' + args.port + '/' + domainadd + (((obj.mtype == 3) && (obj.relaynodeid == null)) ? 'local' : 'mesh') + 'relay.ashx?p=13&auth=' + authCookie // Protocol 13 is Web-SSH-Files
parent.parent.debug('relay', 'SSH: Connection websocket to ' + url);
obj.wsClient = new WebSocket(url, options);
obj.wsClient.on('open', function () { parent.parent.debug('relay', 'SSH: Relay websocket open'); });
@ -1922,7 +1812,7 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
obj.sshClient.on('ready', function () { // Authentication was successful.
// If requested, save the credentials
saveSshCredentials(obj.keep);
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
obj.sessionid = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64');
obj.startTime = Date.now();
// Event start of session
@ -1967,15 +1857,16 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
ws._socket.resume();
}
} else {
try {
if (typeof data == 'string') {
// Forward any ping/pong commands to the browser
var cmd = null;
cmd = JSON.parse(data);
try { cmd = JSON.parse(data); } catch (ex) { }
if ((cmd != null) && (cmd.ctrlChannel == '102938') && ((cmd.type == 'ping') || (cmd.type == 'pong'))) { obj.ws.send(data); }
return;
} catch (ex) { // Relay WS --> SSH
if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
}
// Relay WS --> SSH
if ((data.length > 0) && (obj.ser != null)) { try { obj.ser.updateBuffer(data); } catch (ex) { console.log(ex); } }
}
});
obj.wsClient.on('close', function () {
@ -2270,7 +2161,6 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
if ((user == null) || (req.query.nodeid == null)) { obj.close(); return; } // Invalid nodeid
parent.GetNodeWithRights(domain, user, req.query.nodeid, function (node, rights, visible) {
if (obj.ws == null) return; // obj has been cleaned up, just exit.
node = parent.common.unEscapeLinksFieldName(node); // unEscape node data for rdp/ssh credentials
// Check permissions
if ((rights & 8) == 0) { obj.close(); return; } // No MESHRIGHT_REMOTECONTROL rights
@ -2335,6 +2225,6 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
function checkRelayRights(parent, domain, user, relayNodeId, func) {
if (relayNodeId == null) { func(true); return; } // No relay, do nothing.
parent.GetNodeWithRights(domain, user, relayNodeId, function (node, rights, visible) {
func((node != null) && ((rights & 0x00200008) != 0)); // MESHRIGHT_REMOTECONTROL or MESHRIGHT_RELAY rights
func((node != null) && (rights == 0xFFFFFFFF));
});
}

View file

@ -50,47 +50,6 @@ function createOutFile(args, filename) {
args.out = outputFileName.join('.');
}
// Hash an object
function hashObject(obj) {
if (obj == null) { return null; }
const hash = crypto.createHash('sha384');
if (Buffer.isBuffer(obj)) { hash.update(obj); } else { hash.update(JSON.stringify(obj)); }
return hash.digest().toString('hex');
}
// Load a .bmp file.
function loadBitmap(bitmapFile) {
var bitmapData = null;
try { bitmapData = fs.readFileSync(bitmapFile); } catch (ex) { }
if ((bitmapData == null) || (bitmapData.length < 14) || (bitmapData[0] != 0x42) || (bitmapData[1] != 0x4D)) return null;
return bitmapData.slice(14);
}
// Load a .ico file. This will load all icons in the file into a icon group object
function loadIcon(iconFile) {
var iconData = null;
try { iconData = fs.readFileSync(iconFile); } catch (ex) { }
if ((iconData == null) || (iconData.length < 6) || (iconData[0] != 0) || (iconData[1] != 0)) return null;
const r = { resType: iconData.readUInt16LE(2), resCount: iconData.readUInt16LE(4), icons: {} };
if (r.resType != 1) return null;
var ptr = 6;
for (var i = 1; i <= r.resCount; i++) {
var icon = {};
icon.width = iconData[ptr + 0];
icon.height = iconData[ptr + 1];
icon.colorCount = iconData[ptr + 2];
icon.planes = iconData.readUInt16LE(ptr + 4);
icon.bitCount = iconData.readUInt16LE(ptr + 6);
icon.bytesInRes = iconData.readUInt32LE(ptr + 8);
icon.iconCursorId = i;
const offset = iconData.readUInt32LE(ptr + 12);
icon.icon = iconData.slice(offset, offset + icon.bytesInRes);
r.icons[i] = icon;
ptr += 16;
}
return r;
}
// Load certificates and private key from PEM files
function loadCertificates(pemFileNames) {
var certs = [], keys = [];
@ -298,7 +257,7 @@ function createAuthenticodeHandler(path) {
for (var i = 0; i < obj.header.coff.numberOfSections; i++) {
var section = {};
buf = readFileSlice(obj.header.SectionHeadersPtr + (i * 40), 40);
if ((buf[0] != 46) && (buf[0] != 95)) { obj.close(); return false; }; // Name of the section must start with a dot or underscore. If not, something is wrong.
if (buf[0] != 46) { obj.close(); return false; }; // Name of the section must start with a dot. If not, something is wrong.
var sectionName = buf.slice(0, 8).toString().trim('\0');
var j = sectionName.indexOf('\0');
if (j >= 0) { sectionName = sectionName.substring(0, j); } // Trim any trailing zeroes
@ -609,17 +568,16 @@ function createAuthenticodeHandler(path) {
// Pad the resource section & allocate the buffer
const fileAlign = obj.header.peWindows.fileAlignment
var resSizeTotal = resSizes.tables + resSizes.items + resSizes.names + resSizes.data;
var resNoPadding = resSizeTotal + 4; // TODO: Not sure why this is off by 4
if ((resSizeTotal % fileAlign) != 0) { resSizeTotal += (fileAlign - (resSizeTotal % fileAlign)); }
const resSectionBuffer = Buffer.alloc(resSizeTotal);
// Write the resource section, calling a recursive method
// Write the resource section, calling a recusrize method
const resPointers = { tables: 0, items: resSizes.tables, names: resSizes.tables + resSizes.items, data: resSizes.tables + resSizes.items + resSizes.names };
createResourceSection(resources, resSectionBuffer, resPointers);
//console.log('generateResourceSection', resPointers);
// Done, return the result
return { size: resNoPadding, data: resSectionBuffer };
return resSectionBuffer;
}
// Return the total size of a resource header, this is a recursive method
@ -762,28 +720,6 @@ function createAuthenticodeHandler(path) {
return pkcs7raw;
}
// Get bitmaps information from resource
obj.getBitmapInfo = function () {
const r = {}, ptr = obj.header.sections['.rsrc'].rawAddr;
// Find and parse each icon
const bitmaps = {}
for (var i = 0; i < obj.resources.entries.length; i++) {
if (obj.resources.entries[i].name == resourceDefaultNames.bitmaps) {
for (var j = 0; j < obj.resources.entries[i].table.entries.length; j++) {
const bitmapName = obj.resources.entries[i].table.entries[j].name;
const offsetToData = obj.resources.entries[i].table.entries[j].table.entries[0].item.offsetToData;
const size = obj.resources.entries[i].table.entries[j].table.entries[0].item.size;
const actualPtr = (offsetToData - obj.header.sections['.rsrc'].virtualAddr) + ptr;
bitmaps[bitmapName] = readFileSlice(actualPtr, size);
}
}
}
return bitmaps;
}
// Get icon information from resource
obj.getIconInfo = function () {
const r = {}, ptr = obj.header.sections['.rsrc'].rawAddr;
@ -841,126 +777,9 @@ function createAuthenticodeHandler(path) {
return r;
}
// Set bitmap information
obj.setBitmapInfo = function (bitmapInfo) {
// Delete all bitmaps resources
var resourcesEntries = [];
for (var i = 0; i < obj.resources.entries.length; i++) {
if (obj.resources.entries[i].name != resourceDefaultNames.bitmaps) {
resourcesEntries.push(obj.resources.entries[i]);
}
}
obj.resources.entries = resourcesEntries;
// Add all bitmap entries
const bitmapEntry = { name: resourceDefaultNames.bitmaps, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [] } };
for (var i in bitmapInfo) {
var name = i;
if (parseInt(i) == name) { name = parseInt(i); }
const bitmapItemEntry = { name: name, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [{ name: 1033, item: { buffer: bitmapInfo[i], codePage: 0 } }] } }
bitmapEntry.table.entries.push(bitmapItemEntry);
}
obj.resources.entries.push(bitmapEntry);
// Sort the resources by name. This is required.
function resSort(a, b) {
if ((typeof a == 'string') && (typeof b == 'string')) { if (a < b) return -1; if (a > b) return 1; return 0; }
if ((typeof a == 'number') && (typeof b == 'number')) { return a - b; }
if ((typeof a == 'string') && (typeof b == 'number')) { return -1; }
return 1;
}
const names = [];
for (var i = 0; i < obj.resources.entries.length; i++) { names.push(obj.resources.entries[i].name); }
names.sort(resSort);
var newEntryOrder = [];
for (var i in names) {
for (var j = 0; j < obj.resources.entries.length; j++) {
if (obj.resources.entries[j].name == names[i]) { newEntryOrder.push(obj.resources.entries[j]); }
}
}
obj.resources.entries = newEntryOrder;
}
// Set icon information
obj.setIconInfo = function (iconInfo) {
// Delete all icon and icon groups resources
var resourcesEntries = [];
for (var i = 0; i < obj.resources.entries.length; i++) {
if ((obj.resources.entries[i].name != resourceDefaultNames.icon) && (obj.resources.entries[i].name != resourceDefaultNames.iconGroups)) {
resourcesEntries.push(obj.resources.entries[i]);
}
}
obj.resources.entries = resourcesEntries;
// Count the icon groups and re-number all icons
var iconGroupCount = 0, nextIconNumber = 1;
for (var i in iconInfo) {
iconGroupCount++;
var xicons = {};
for (var j in iconInfo[i].icons) { xicons[nextIconNumber++] = iconInfo[i].icons[j]; }
iconInfo[i].icons = xicons;
}
if (iconGroupCount == 0) return; // If there are no icon groups, we are done
// Add the new icons entry
const iconsEntry = { name: resourceDefaultNames.icon, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [] } };
for (var i in iconInfo) {
for (var j in iconInfo[i].icons) {
var name = j;
if (parseInt(j) == name) { name = parseInt(j); }
const iconItemEntry = { name: name, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [{ name: 1033, item: { buffer: iconInfo[i].icons[j].icon, codePage: 0 } }] } }
iconsEntry.table.entries.push(iconItemEntry);
}
}
obj.resources.entries.push(iconsEntry);
// Add the new icon group entry
const groupEntry = { name: resourceDefaultNames.iconGroups, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [] } };
for (var i in iconInfo) {
// Build icon group struct
var iconCount = 0, p = 6;
for (var j in iconInfo[i].icons) { iconCount++; }
const buf = Buffer.alloc(6 + (iconCount * 14));
buf.writeUInt16LE(iconInfo[i].resType, 2);
buf.writeUInt16LE(iconCount, 4);
for (var j in iconInfo[i].icons) {
buf[p] = iconInfo[i].icons[j].width;
buf[p + 1] = iconInfo[i].icons[j].height;
buf[p + 2] = iconInfo[i].icons[j].colorCount;
buf.writeUInt16LE(iconInfo[i].icons[j].planes, p + 4);
buf.writeUInt16LE(iconInfo[i].icons[j].bitCount, p + 6);
buf.writeUInt32LE(iconInfo[i].icons[j].bytesInRes, p + 8);
buf.writeUInt16LE(j, p + 12);
p += 14;
}
var name = i;
if (parseInt(i) == name) { name = parseInt(i); }
const groupItemEntry = { name: name, table: { characteristics: 0, timeDateStamp: 0, majorVersion: 0, minorVersion: 0, entries: [{ name: 1033, item: { buffer: buf, codePage: 0 } }] } }
groupEntry.table.entries.push(groupItemEntry);
}
obj.resources.entries.push(groupEntry);
// Sort the resources by name. This is required.
function resSort(a, b) {
if ((typeof a == 'string') && (typeof b == 'string')) { if (a < b) return -1; if (a > b) return 1; return 0; }
if ((typeof a == 'number') && (typeof b == 'number')) { return a - b; }
if ((typeof a == 'string') && (typeof b == 'number')) { return -1; }
return 1;
}
const names = [];
for (var i = 0; i < obj.resources.entries.length; i++) { names.push(obj.resources.entries[i].name); }
names.sort(resSort);
var newEntryOrder = [];
for (var i in names) {
for (var j = 0; j < obj.resources.entries.length; j++) {
if (obj.resources.entries[j].name == names[i]) { newEntryOrder.push(obj.resources.entries[j]); }
}
}
obj.resources.entries = newEntryOrder;
}
// Decode the version information from the resource
obj.getVersionInfo = function () {
//console.log('READ', getVersionInfoData().toString('hex'));
var r = {}, info = readVersionInfo(getVersionInfoData(), 0);
if ((info == null) || (info.stringFiles == null)) return null;
var StringFileInfo = null;
@ -968,8 +787,6 @@ function createAuthenticodeHandler(path) {
if ((StringFileInfo == null) || (StringFileInfo.stringTable == null) || (StringFileInfo.stringTable.strings == null)) return null;
const strings = StringFileInfo.stringTable.strings;
for (var i in strings) { r[strings[i].key] = strings[i].value; }
r['~FileVersion'] = (info.fixedFileInfo.dwFileVersionMS >> 16) + '.' + (info.fixedFileInfo.dwFileVersionMS & 0xFFFF) + '.' + (info.fixedFileInfo.dwFileVersionLS >> 16) + '.' + (info.fixedFileInfo.dwFileVersionLS & 0xFFFF);
r['~ProductVersion'] = (info.fixedFileInfo.dwProductVersionMS >> 16) + '.' + (info.fixedFileInfo.dwProductVersionMS & 0xFFFF) + '.' + (info.fixedFileInfo.dwProductVersionLS >> 16) + '.' + (info.fixedFileInfo.dwProductVersionLS & 0xFFFF);
return r;
}
@ -977,7 +794,7 @@ function createAuthenticodeHandler(path) {
obj.setVersionInfo = function (versions) {
// Convert the version information into a string array
const stringArray = [];
for (var i in versions) { if (!i.startsWith('~')) { stringArray.push({ key: i, value: versions[i] }); } }
for (var i in versions) { stringArray.push({ key: i, value: versions[i] }); }
// Get the existing version data and switch the strings to the new strings
var r = {}, info = readVersionInfo(getVersionInfoData(), 0);
@ -987,20 +804,6 @@ function createAuthenticodeHandler(path) {
if ((StringFileInfo == null) || (StringFileInfo.stringTable == null) || (StringFileInfo.stringTable.strings == null)) return;
StringFileInfo.stringTable.strings = stringArray;
// Set the file version
if (versions['~FileVersion'] != null) {
const FileVersionSplit = versions['~FileVersion'].split('.');
info.fixedFileInfo.dwFileVersionMS = (parseInt(FileVersionSplit[0]) << 16) + parseInt(FileVersionSplit[1]);
info.fixedFileInfo.dwFileVersionLS = (parseInt(FileVersionSplit[2]) << 16) + parseInt(FileVersionSplit[3]);
}
// Set the product version
if (versions['~ProductVersion'] != null) {
const ProductVersionSplit = versions['~ProductVersion'].split('.');
info.fixedFileInfo.dwProductVersionMS = (parseInt(ProductVersionSplit[0]) << 16) + parseInt(ProductVersionSplit[1]);
info.fixedFileInfo.dwProductVersionLS = (parseInt(ProductVersionSplit[2]) << 16) + parseInt(ProductVersionSplit[3]);
}
// Re-encode the version information into a buffer
var verInfoResBufArray = [];
writeVersionInfo(verInfoResBufArray, info);
@ -1339,21 +1142,6 @@ function createAuthenticodeHandler(path) {
return hash.digest();
}
// Hash the file using the selected hashing system skipping resource section
// This hash skips the executables CRC, sections table, resource section, code signing data and signing block
obj.getHashOfSection = function (algo, sectionName) {
if (obj.header.sections[sectionName] == null) return null;
// Get the section start and size
const sectionPtr = obj.header.sections[sectionName].rawAddr;
const sectionSize = obj.header.sections[sectionName].rawSize;
// Hash the remaining data
const hash = crypto.createHash(algo);
runHash(hash, sectionPtr, sectionPtr + sectionSize);
return hash.digest();
}
// Hash the file from start to end loading 64k chunks
function runHash(hash, start, end) {
var ptr = start;
@ -1447,7 +1235,7 @@ function createAuthenticodeHandler(path) {
if (args.hash == 'sha512') { hashOid = forge.pki.oids.sha512; fileHash = obj.getHash('sha512'); }
if (args.hash == 'sha224') { hashOid = forge.pki.oids.sha224; fileHash = obj.getHash('sha224'); }
if (args.hash == 'md5') { hashOid = forge.pki.oids.md5; fileHash = obj.getHash('md5'); }
if (hashOid == null) { func('Invalid signing hash: ' + args.hash); return; };
if (hashOid == null) { func(false); return; };
// Create the signature block
var xp7 = forge.pkcs7.createSignedData();
@ -1566,11 +1354,7 @@ function createAuthenticodeHandler(path) {
options.protocol = timeServerUrl.protocol;
options.hostname = timeServerUrl.hostname;
options.path = timeServerUrl.pathname;
let http = require("http")
if (options.protocol === "https:"){
http = require("https")
}
options.port = ((timeServerUrl.port == '') ? (options.protocol === "https:" ? 443 : 80) : parseInt(timeServerUrl.port));
options.port = ((timeServerUrl.port == '') ? 80 : parseInt(timeServerUrl.port));
if (options.proxy == null) {
// No proxy needed
@ -1588,7 +1372,7 @@ function createAuthenticodeHandler(path) {
// Set up the request
var responseAccumulator = '';
var req = http.request(options, function (res) {
var req = require('http').request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (chunk) { responseAccumulator += chunk; });
res.on('end', function () { func(null, responseAccumulator); });
@ -1609,12 +1393,12 @@ function createAuthenticodeHandler(path) {
proxyOptions.protocol = proxyUrl.protocol;
proxyOptions.hostname = proxyUrl.hostname;
proxyOptions.path = options.hostname + ':' + options.port;
proxyOptions.port = ((proxyUrl.port == '') ? (options.protocol === "https:" ? 443 : 80) : parseInt(proxyUrl.port));
proxyOptions.port = ((proxyUrl.port == '') ? 80 : parseInt(proxyUrl.port));
}
// Set up the proxy request
var responseAccumulator = '';
var req = http.request(proxyOptions);
var req = require('http').request(proxyOptions);
req.on('error', function (err) { func('' + err); });
req.on('connect', function (res, socket, head) {
// Make a request over the HTTP tunnel
@ -1669,7 +1453,7 @@ function createAuthenticodeHandler(path) {
// Open the output file
var output = null;
try { output = fs.openSync(args.out, 'w+'); } catch (ex) { }
if (output == null) { func('Unable to open output file: ' + args.out); return; }
if (output == null) { func(false); return; }
var tmp, written = 0, executableSize = obj.header.sigpos ? obj.header.sigpos : filesize;
// Compute pre-header length and copy that to the new file
@ -1747,16 +1531,6 @@ function createAuthenticodeHandler(path) {
fs.closeSync(output);
}
// Find where a directory value is in the old sections and map it to the new sections
function correctDirectoryValue(oldSections, newSections, value) {
for (var i in oldSections) {
if ((value >= oldSections[i].virtualAddr) && (value < (oldSections[i].virtualAddr + oldSections[i].virtualSize))) {
return newSections[i].virtualAddr + (value - oldSections[i].virtualAddr);
}
}
return 0;
}
// Save the executable
obj.writeExecutable = function (args, cert, func) {
// Open the file
@ -1767,90 +1541,51 @@ function createAuthenticodeHandler(path) {
var fullHeaderLen = obj.header.SectionHeadersPtr + (obj.header.coff.numberOfSections * 40);
var fullHeader = readFileSlice(written, fullHeaderLen);
// Create the resource section
const rsrcSectionX = generateResourceSection(obj.resources); // This section is created with padding already included
var rsrcSection = rsrcSectionX.data;
var rsrcSectionVirtualSize = rsrcSectionX.size;
var rsrcSectionRawSize = rsrcSection.length;
// Calculate the location and original and new size of the resource segment
var fileAlign = obj.header.peWindows.fileAlignment
var resPtr = obj.header.sections['.rsrc'].rawAddr;
var oldResSize = obj.header.sections['.rsrc'].rawSize;
var newResSize = rsrcSection.length;
var newResSize = obj.header.sections['.rsrc'].rawSize; // Testing 102400
var resDeltaSize = newResSize - oldResSize;
// Compute the sizeOfInitializedData
var sizeOfInitializedData = 0;
for (var i in obj.header.sections) {
if (i != '.text') {
if (i == '.rsrc') {
sizeOfInitializedData += rsrcSectionRawSize;
} else {
sizeOfInitializedData += obj.header.sections[i].rawSize;
}
}
}
// Change PE optional header sizeOfInitializedData standard field
fullHeader.writeUInt32LE(sizeOfInitializedData, obj.header.peOptionalHeaderLocation + 8);
fullHeader.writeUInt32LE(obj.header.peStandard.sizeOfInitializedData + resDeltaSize, obj.header.peOptionalHeaderLocation + 8);
fullHeader.writeUInt32LE(obj.header.peWindows.sizeOfImage, obj.header.peOptionalHeaderLocation + 56); // TODO: resDeltaSize
// Update the checksum to zero
fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 64);
// We are going to setup the old a new sections here, we need this to correct directory values
var oldSections = obj.header.sections;
var newSections = {};
// Make changes to the segments table
var virtualAddress = 4096;
for (var i in obj.header.sections) {
const section = obj.header.sections[i];
newSections[i] = { virtualSize: section.virtualSize };
if (i == '.rsrc') {
// Change the size of the resource section
fullHeader.writeUInt32LE(rsrcSectionVirtualSize, section.ptr + 8); // virtualSize
fullHeader.writeUInt32LE(rsrcSectionRawSize, section.ptr + 16); // rawSize
// Set the virtual address of the section
fullHeader.writeUInt32LE(virtualAddress, section.ptr + 12); // Virtual address
newSections[i].virtualAddr = virtualAddress;
var virtualAddressPadding = (rsrcSectionVirtualSize % 4096);
virtualAddress += rsrcSectionVirtualSize;
if (virtualAddressPadding != 0) { virtualAddress += (4096 - virtualAddressPadding); }
} else {
// Change the location of any other section if located after the resource section
if (section.rawAddr > resPtr) { fullHeader.writeUInt32LE(section.rawAddr + resDeltaSize, section.ptr + 20); }
// Set the virtual address of the section
fullHeader.writeUInt32LE(virtualAddress, section.ptr + 12); // Virtual address
newSections[i].virtualAddr = virtualAddress;
var virtualAddressPadding = (section.virtualSize % 4096);
virtualAddress += section.virtualSize;
if (virtualAddressPadding != 0) { virtualAddress += (4096 - virtualAddressPadding); }
}
}
// Make change to the data directories header to fix resource segment size and add/remove signature
const pePlusOffset = (obj.header.pe32plus == 0) ? 0 : 16; // This header is the same for 32 and 64 bit, but 64bit is offset by 16 bytes.
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.exportTable.addr), obj.header.peOptionalHeaderLocation + 96 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.importTable.addr), obj.header.peOptionalHeaderLocation + 104 + pePlusOffset);
fullHeader.writeUInt32LE(rsrcSectionVirtualSize, obj.header.peOptionalHeaderLocation + 116 + pePlusOffset); // Change the resource segment size
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.exceptionTableAddr.addr), obj.header.peOptionalHeaderLocation + 120 + pePlusOffset);
if (obj.header.dataDirectories.exportTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.exportTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 96 + pePlusOffset); }
if (obj.header.dataDirectories.importTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.importTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 104 + pePlusOffset); }
//fullHeader.writeUInt32LE(obj.header.dataDirectories.resourceTable.size + resDeltaSize, obj.header.peOptionalHeaderLocation + 116 + pePlusOffset); // Change the resource segment size
if (obj.header.dataDirectories.exceptionTableAddr.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.exceptionTableAddr.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 120 + pePlusOffset); }
fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 128 + pePlusOffset); // certificate table addr (TODO)
fullHeader.writeUInt32LE(0, obj.header.peOptionalHeaderLocation + 132 + pePlusOffset); // certificate table size (TODO)
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.baseRelocationTable.addr), obj.header.peOptionalHeaderLocation + 136 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.debug.addr), obj.header.peOptionalHeaderLocation + 144 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.globalPtr.addr), obj.header.peOptionalHeaderLocation + 160 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.tLSTable.addr), obj.header.peOptionalHeaderLocation + 168 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.loadConfigTable.addr), obj.header.peOptionalHeaderLocation + 176 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.boundImport.addr), obj.header.peOptionalHeaderLocation + 184 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.iAT.addr), obj.header.peOptionalHeaderLocation + 192 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.delayImportDescriptor.addr), obj.header.peOptionalHeaderLocation + 200 + pePlusOffset);
fullHeader.writeUInt32LE(correctDirectoryValue(oldSections, newSections, obj.header.dataDirectories.clrRuntimeHeader.addr), obj.header.peOptionalHeaderLocation + 208 + pePlusOffset);
if (obj.header.dataDirectories.baseRelocationTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.baseRelocationTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 136 + pePlusOffset); }
if (obj.header.dataDirectories.debug.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.debug.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 144 + pePlusOffset); }
if (obj.header.dataDirectories.globalPtr.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.globalPtr.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 160 + pePlusOffset); }
if (obj.header.dataDirectories.tLSTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.tLSTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 168 + pePlusOffset); }
if (obj.header.dataDirectories.loadConfigTable.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.loadConfigTable.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 176 + pePlusOffset); }
if (obj.header.dataDirectories.boundImport.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.boundImport.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 184 + pePlusOffset); }
if (obj.header.dataDirectories.iAT.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.iAT.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 192 + pePlusOffset); }
if (obj.header.dataDirectories.delayImportDescriptor.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.delayImportDescriptor.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 200 + pePlusOffset); }
if (obj.header.dataDirectories.clrRuntimeHeader.addr > resPtr) { fullHeader.writeUInt32LE(obj.header.dataDirectories.clrRuntimeHeader.addr + resDeltaSize, obj.header.peOptionalHeaderLocation + 208 + pePlusOffset); }
// Write size of image. We put the next virtual address.
fullHeader.writeUInt32LE(virtualAddress, obj.header.peOptionalHeaderLocation + 56); // sizeOfImage
// Make changes to the segments table
for (var i in obj.header.sections) {
const section = obj.header.sections[i];
if (i == '.rsrc') {
// Change the size of the resource section
fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 8); // virtualSize (TODO)
fullHeader.writeUInt32LE(section.rawSize + resDeltaSize, section.ptr + 16); // rawSize
} else {
// Change the location of any other section if located after the resource section
if (section.virtualAddr > resPtr) { fullHeader.writeUInt32LE(section.virtualAddr + resDeltaSize, section.ptr + 12); }
if (section.rawAddr > resPtr) { fullHeader.writeUInt32LE(section.rawAddr + resDeltaSize, section.ptr + 20); }
}
}
// Write the entire header to the destination file
//console.log('Write header', fullHeader.length, written);
@ -1867,6 +1602,7 @@ function createAuthenticodeHandler(path) {
}
// Write the new resource section
var rsrcSection = generateResourceSection(obj.resources);
fs.writeSync(output, rsrcSection);
written += rsrcSection.length;
//console.log('Write res', rsrcSection.length, written);
@ -2070,58 +1806,40 @@ function start() {
console.log(" node authenticode.js [command] [options]");
console.log("Commands:");
console.log(" info: Show information about an executable.");
console.log(" --exe [file] Required executable to view information.");
console.log(" --json Show information in JSON format.");
console.log(" --exe [file] Required executable to view information.");
console.log(" --json Show information in JSON format.");
console.log(" sign: Sign an executable.");
console.log(" --exe [file] Required executable to sign.");
console.log(" --out [file] Resulting signed executable.");
console.log(" --pem [pemfile] Certificate & private key to sign the executable with.");
console.log(" --desc [description] Description string to embbed into signature.");
console.log(" --url [url] URL to embbed into signature.");
console.log(" --hash [method] Default is SHA384, possible value: MD5, SHA224, SHA256, SHA384 or SHA512.");
console.log(" --time [url] The time signing server URL.");
console.log(" --proxy [url] The HTTP proxy to use to contact the time signing server, must start with http://");
console.log(" --exe [file] Required executable to sign.");
console.log(" --out [file] Resulting signed executable.");
console.log(" --pem [pemfile] Certificate & private key to sign the executable with.");
console.log(" --desc [description] Description string to embbed into signature.");
console.log(" --url [url] URL to embbed into signature.");
console.log(" --hash [method] Default is SHA384, possible value: MD5, SHA224, SHA256, SHA384 or SHA512.");
console.log(" --time [url] The time signing server URL.");
console.log(" --proxy [url] The HTTP proxy to use to contact the time signing server, must start with http://");
console.log(" unsign: Remove the signature from the executable.");
console.log(" --exe [file] Required executable to un-sign.");
console.log(" --out [file] Resulting executable with signature removed.");
console.log(" --exe [file] Required executable to un-sign.");
console.log(" --out [file] Resulting executable with signature removed.");
console.log(" createcert: Create a code signging self-signed certificate and key.");
console.log(" --out [pemfile] Required certificate file to create.");
console.log(" --cn [value] Required certificate common name.");
console.log(" --country [value] Certificate country name.");
console.log(" --state [value] Certificate state name.");
console.log(" --locality [value] Certificate locality name.");
console.log(" --org [value] Certificate organization name.");
console.log(" --ou [value] Certificate organization unit name.");
console.log(" --serial [value] Certificate serial number.");
console.log(" --out [pemfile] Required certificate file to create.");
console.log(" --cn [value] Required certificate common name.");
console.log(" --country [value] Certificate country name.");
console.log(" --state [value] Certificate state name.");
console.log(" --locality [value] Certificate locality name.");
console.log(" --org [value] Certificate organization name.");
console.log(" --ou [value] Certificate organization unit name.");
console.log(" --serial [value] Certificate serial number.");
console.log(" timestamp: Add a signed timestamp to an already signed executable.");
console.log(" --exe [file] Required executable to timestamp.");
console.log(" --out [file] Resulting signed executable.");
console.log(" --time [url] The time signing server URL.");
console.log(" --proxy [url] The HTTP proxy to use to contact the time signing server, must start with http://");
console.log(" bitmaps: Show bitmap resources in the executable.");
console.log(" --exe [file] Input executable.");
console.log(" savebitmap: Save a single bitmap to a .bmp file.");
console.log(" --exe [file] Input executable.");
console.log(" --out [file] Resulting .ico file.");
console.log(" --bitmap [number] Bitmap number to save to file.");
console.log(" icons: Show the icon resources in the executable.");
console.log(" --exe [file] Input executable.");
console.log(" saveicon: Save a single icon bitmap to a .ico file.");
console.log(" --exe [file] Input executable.");
console.log(" --out [file] Resulting .ico file.");
console.log(" --icon [number] Icon number to save to file.");
console.log(" saveicons: Save an icon group to a .ico file.");
console.log(" --exe [file] Input executable.");
console.log(" --out [file] Resulting .ico file.");
console.log(" --icongroup [groupNumber] Icon groupnumber to save to file.");
console.log(" --exe [file] Required executable to sign.");
console.log(" --out [file] Resulting signed executable.");
console.log(" --time [url] The time signing server URL.");
console.log(" --proxy [url] The HTTP proxy to use to contact the time signing server, must start with http://");
console.log("");
console.log("Note that certificate PEM files must first have the signing certificate,");
console.log("followed by all certificates that form the trust chain.");
console.log("");
console.log("When doing sign/unsign, you can also change resource properties of the generated file.");
console.log("");
console.log(" --fileversionnumber n.n.n.n");
console.log(" --productversionnumber n.n.n.n");
console.log(" --filedescription [value]");
console.log(" --fileversion [value]");
console.log(" --internalname [value]");
@ -2129,15 +1847,11 @@ function start() {
console.log(" --originalfilename [value]");
console.log(" --productname [value]");
console.log(" --productversion [value]");
console.log(" --removeicongroup [number]");
console.log(" --removebitmap [number]");
console.log(" --icon [groupNumber],[filename.ico]");
console.log(" --bitmap [number],[filename.bmp]");
return;
}
// Check that a valid command is passed in
if (['info', 'sign', 'unsign', 'createcert', 'icons', 'bitmaps', 'saveicon', 'saveicons', 'savebitmap', 'header', 'sections', 'timestamp', 'signblock'].indexOf(process.argv[2].toLowerCase()) == -1) {
if (['info', 'sign', 'unsign', 'createcert', 'icons', 'saveicon', 'header', 'timestamp', 'signblock'].indexOf(process.argv[2].toLowerCase()) == -1) {
console.log("Invalid command: " + process.argv[2]);
console.log("Valid commands are: info, sign, unsign, createcert, timestamp");
return;
@ -2153,7 +1867,7 @@ function start() {
if (exe == null) { console.log("Unable to parse executable file: " + args.exe); return; }
}
// Parse the string resources and make any required changes
// Parse the resources and make any required changes
var resChanges = false, versionStrings = null;
if (exe != null) {
versionStrings = exe.getVersionInfo();
@ -2162,88 +1876,16 @@ function start() {
const prop = versionProperties[i], propl = prop.toLowerCase();
if (args[propl] && (args[propl] != versionStrings[prop])) { versionStrings[prop] = args[propl]; resChanges = true; }
}
if (args['fileversionnumber'] != null) {
const fileVerSplit = args['fileversionnumber'].split('.');
if (fileVerSplit.length != 4) { console.log("--fileversionnumber must be of format n.n.n.n, for example: 1.2.3.4"); return; }
for (var i in fileVerSplit) { var n = parseInt(fileVerSplit[i]); if ((n < 0) || (n > 65535)) { console.log("--fileversionnumber numbers must be between 0 and 65535."); return; } }
if (args['fileversionnumber'] != versionStrings['~FileVersion']) { versionStrings['~FileVersion'] = args['fileversionnumber']; resChanges = true; }
}
if (args['productversionnumber'] != null) {
const productVerSplit = args['productversionnumber'].split('.');
if (productVerSplit.length != 4) { console.log("--productversionnumber must be of format n.n.n.n, for example: 1.2.3.4"); return; }
for (var i in productVerSplit) { var n = parseInt(productVerSplit[i]); if ((n < 0) || (n > 65535)) { console.log("--productversionnumber numbers must be between 0 and 65535."); return; } }
if (args['productversionnumber'] != versionStrings['~ProductVersion']) { versionStrings['~ProductVersion'] = args['productversionnumber']; resChanges = true; }
}
if (resChanges == true) { exe.setVersionInfo(versionStrings); }
}
// Parse the icon changes
resChanges = false;
var icons = null, bitmaps = null;
if (exe != null) {
icons = exe.getIconInfo();
bitmaps = exe.getBitmapInfo();
if (typeof args['removeicongroup'] == 'string') { // If --removeicongroup is used, it's to remove an existing icon group
const groupsToRemove = args['removeicongroup'].split(',');
for (var i in groupsToRemove) { if (icons[groupsToRemove[i]] != null) { delete icons[groupsToRemove[i]]; resChanges = true; } }
} else if (typeof args['removeicongroup'] == 'number') {
if (icons[args['removeicongroup']] != null) { delete icons[args['removeicongroup']]; resChanges = true; }
}
if (typeof args['removebitmap'] == 'string') { // If --removebitmap is used
const bitmapsToRemove = args['removebitmap'].split(',');
for (var i in bitmapsToRemove) { if (bitmaps[bitmapsToRemove[i]] != null) { delete bitmaps[bitmapsToRemove[i]]; resChanges = true; } }
} else if (typeof args['removebitmap'] == 'number') {
if (bitmaps[args['removebitmap']] != null) { delete bitmaps[args['removebitmap']]; resChanges = true; }
}
if (typeof args['icon'] == 'string') { // If --icon is used, it's to add or replace an existing icon group
const iconToAddSplit = args['icon'].split(',');
if (iconToAddSplit.length != 2) { console.log("The --icon format is: --icon [number],[file]."); return; }
const iconName = parseInt(iconToAddSplit[0]);
const iconFile = iconToAddSplit[1];
const icon = loadIcon(iconFile);
if (icon == null) { console.log("Unable to load icon: " + iconFile); return; }
if (icons[iconName] != null) {
const iconHash = hashObject(icon); // Compute the new icon group hash
const iconHash2 = hashObject(icons[iconName]); // Computer the old icon group hash
if (iconHash != iconHash2) { icons[iconName] = icon; resChanges = true; } // If different, replace the icon group
} else {
icons[iconName] = icon; // We are adding an icon group
resChanges = true;
}
}
if (typeof args['bitmap'] == 'string') { // If --bitmap is used, it's to add or replace an existing bitmap
const bitmapToAddSplit = args['bitmap'].split(',');
if (bitmapToAddSplit.length != 2) { console.log("The --bitmap format is: --bitmap [number],[file]."); return; }
const bitmapName = parseInt(bitmapToAddSplit[0]);
const bitmapFile = bitmapToAddSplit[1];
const bitmap = loadBitmap(bitmapFile);
if (bitmap == null) { console.log("Unable to load bitmap: " + bitmapFile); return; }
if (bitmaps[bitmapName] != null) {
const bitmapHash = hashObject(bitmap); // Compute the new bitmap hash
const bitmapHash2 = hashObject(bitmaps[bitmapName]); // Computer the old bitmap hash
if (bitmapHash != bitmapHash2) { bitmaps[bitmapName] = bitmap; resChanges = true; } // If different, replace the new bitmap
} else {
bitmaps[bitmapName] = bitmap; // We are adding an new bitmap
resChanges = true;
}
}
if (resChanges == true) {
exe.setIconInfo(icons);
exe.setBitmapInfo(bitmaps);
}
}
// Execute the command
var command = process.argv[2].toLowerCase();
if (command == 'info') { // Get signature information about an executable
if (exe == null) { console.log("Missing --exe [filename]"); return; }
if (args.json) {
var r = {}, stringInfo = exe.getVersionInfo();
if (stringInfo != null) {
r.versionInfo = {};
r.stringInfo = {};
for (var i in stringInfo) { if (i.startsWith('~')) { r.versionInfo[i.substring(1)] = stringInfo[i]; } else { r.stringInfo[i] = stringInfo[i]; } }
}
var r = {}, versionInfo = exe.getVersionInfo();
if (versionInfo != null) { r.versionInfo = versionInfo; }
if (exe.fileHashAlgo != null) {
r.signture = {};
if (exe.fileHashAlgo != null) { r.signture.hashMethod = exe.fileHashAlgo; }
@ -2254,12 +1896,7 @@ function start() {
console.log(JSON.stringify(r, null, 2));
} else {
var versionInfo = exe.getVersionInfo();
if (versionInfo != null) {
console.log("Version Information:");
for (var i in versionInfo) { if (i.startsWith('~') == true) { console.log(' ' + i.substring(1) + ': ' + versionInfo[i] + ''); } }
console.log("String Information:");
for (var i in versionInfo) { if (i.startsWith('~') == false) { if (versionInfo[i] == null) { console.log(' ' + i + ': (Empty)'); } else { console.log(' ' + i + ': \"' + versionInfo[i] + '\"'); } } }
}
if (versionInfo != null) { console.log("Version Information:"); for (var i in versionInfo) { if (versionInfo[i] == null) { console.log(' ' + i + ': (Empty)'); } else { console.log(' ' + i + ': \"' + versionInfo[i] + '\"'); } } }
console.log("Checksum Information:");
console.log(" Header CheckSum: 0x" + exe.header.peWindows.checkSum.toString(16));
console.log(" Actual CheckSum: 0x" + exe.header.peWindows.checkSumActual.toString(16));
@ -2277,26 +1914,6 @@ function start() {
if (command == 'header') { // Display the full executable header in JSON format
if (exe == null) { console.log("Missing --exe [filename]"); return; }
console.log(exe.header);
// Check that the header is valid
var ptr = 1024, sizeOfCode = 0, sizeOfInitializedData = 0;
for (var i in exe.header.sections) {
if (i == '.text') { sizeOfCode += exe.header.sections[i].rawSize; } else { sizeOfInitializedData += exe.header.sections[i].rawSize; }
if (exe.header.sections[i].rawAddr != ptr) { console.log('WARNING: ' + i + ' section should have a rawAddr or ' + ptr + ', but has ' + exe.header.sections[i].rawAddr + ' instead.'); }
ptr += exe.header.sections[i].rawSize;
}
if (exe.header.peStandard.sizeOfCode != sizeOfCode) { console.log('WARNING: Size of code is ' + exe.header.peStandard.sizeOfCode + ', should be ' + sizeOfCode + '.'); }
if (exe.header.peStandard.sizeOfInitializedData != sizeOfInitializedData) { console.log('WARNING: Size of initialized data is ' + exe.header.peStandard.sizeOfInitializedData + ', should be ' + sizeOfInitializedData + '.'); }
}
if (command == 'sections') { // Display sections in CSV format
if (exe == null) { console.log("Missing --exe [filename]"); return; }
var csvHeader = 'section';
for (var i in exe.header.sections['.text']) { csvHeader += ',' + i; }
console.log(csvHeader);
for (var i in exe.header.sections) {
var csvData = i;
for (var j in exe.header.sections[i]) { csvData += ',' + exe.header.sections[i][j]; }
console.log(csvData);
}
}
if (command == 'sign') { // Sign an executable
if (typeof args.exe != 'string') { console.log("Missing --exe [filename]"); return; }
@ -2327,7 +1944,7 @@ function start() {
if (resChanges == false) {
if (exe.header.signed) {
console.log("Unsigning to " + args.out);
exe.unsign(args); // Simple unsign, copy most of the original file.
exe.unsign(args); // Simple unsign, copy most of the original file.
console.log("Done.");
} else {
console.log("Executable is not signed.");
@ -2350,32 +1967,6 @@ function start() {
fs.writeFileSync(args.out, pki.certificateToPem(cert.cert) + '\r\n' + pki.privateKeyToPem(cert.key));
console.log("Done.");
}
if (command == 'bitmaps') { // Show bitmaps in the executable
if (exe == null) { console.log("Missing --exe [filename]"); return; }
if (args.json) {
var bitmapInfo = exe.getBitmapInfo();
console.log(JSON.stringify(bitmapInfo, null, 2));
} else {
var bitmapInfo = exe.getBitmapInfo();
if (bitmapInfo != null) {
console.log("Bitmap Information:");
for (var i in bitmapInfo) { console.log(' ' + i + ': ' + bitmapInfo[i].length + ' byte' + ((bitmapInfo[i].length > 1) ? 's' : '') + '.'); }
}
}
}
if (command == 'savebitmap') { // Save an bitmap to file
if (exe == null) { console.log("Missing --exe [filename]"); return; }
if (typeof args.out != 'string') { console.log("Missing --out [filename]"); return; }
if (typeof args.bitmap != 'number') { console.log("Missing or incorrect --bitmap [number]"); return; }
const bitmapInfo = exe.getBitmapInfo();
if (bitmapInfo[args.bitmap] == null) { console.log("Unknown bitmap: " + args.bitmap); return; }
console.log("Writing to " + args.out);
var bitmapHeader = Buffer.from('424D000000000000000036000000', 'hex');
bitmapHeader.writeUInt32LE(14 + bitmapInfo[args.bitmap].length, 2); // Write the full size of the bitmap file
fs.writeFileSync(args.out, Buffer.concat([bitmapHeader, bitmapInfo[args.bitmap]]));
console.log("Done.");
}
if (command == 'icons') { // Show icons in the executable
if (exe == null) { console.log("Missing --exe [filename]"); return; }
if (args.json) {
@ -2415,48 +2006,6 @@ function start() {
fs.writeFileSync(args.out, Buffer.concat([buf, icon.icon]));
console.log("Done.");
}
if (command == 'saveicons') { // Save an icon group to file
if (exe == null) { console.log("Missing --exe [filename]"); return; }
if (typeof args.out != 'string') { console.log("Missing --out [filename]"); return; }
if (typeof args.icongroup != 'number') { console.log("Missing or incorrect --icongroup [number]"); return; }
const iconInfo = exe.getIconInfo();
const iconGroup = iconInfo[args.icongroup];
if (iconGroup == null) { console.log("Invalid or incorrect --icongroup [number]"); return; }
// Count the number of icons in the group
var iconCount = 0;
for (var i in iconGroup.icons) { iconCount++; }
// .ico header: https://en.wikipedia.org/wiki/ICO_(file_format)
const iconFileData = [];
const header = Buffer.alloc(6);
header.writeUInt16LE(1, 2); // 1 = Icon, 2 = Cursor
header.writeUInt16LE(iconCount, 4); // Icon Count, always 1 in our case
iconFileData.push(header);
// Store each icon header
var offsetPtr = 6 + (16 * iconCount);
for (var i in iconGroup.icons) {
const buf = Buffer.alloc(16);
buf[0] = iconGroup.icons[i].width; // Width (0 = 256)
buf[1] = iconGroup.icons[i].height; // Height (0 = 256)
buf[2] = iconGroup.icons[i].colorCount; // Colors
buf.writeUInt16LE(iconGroup.icons[i].planes, 4); // Color planes
buf.writeUInt16LE(iconGroup.icons[i].bitCount, 6); // Bits per pixel
buf.writeUInt32LE(iconGroup.icons[i].icon.length, 8); // Size
buf.writeUInt32LE(offsetPtr, 12); // Offset
offsetPtr += iconGroup.icons[i].icon.length;
iconFileData.push(buf);
}
// Store each icon
for (var i in iconGroup.icons) { iconFileData.push(iconGroup.icons[i].icon); }
// Write the .ico file
console.log("Writing to " + args.out);
fs.writeFileSync(args.out, Buffer.concat(iconFileData));
console.log("Done.");
}
if (command == 'signblock') { // Display the raw signature block of the executable in hex
if (exe == null) { console.log("Missing --exe [filename]"); return; }
var buf = exe.getRawSignatureBlock();
@ -2485,6 +2034,4 @@ if (require.main === module) { start(); }
// Exports
module.exports.createAuthenticodeHandler = createAuthenticodeHandler;
module.exports.loadCertificates = loadCertificates;
module.exports.loadIcon = loadIcon;
module.exports.loadBitmap = loadBitmap;
module.exports.hashObject = hashObject;

View file

@ -1,2 +0,0 @@
#! /usr/bin/env node
require("../meshcentral.js");

View file

@ -247,25 +247,16 @@ module.exports.CertificateOperations = function (parent) {
// Get the certificate common name
var certCommonName = r.certs[0].subject.getField('CN');
if (certCommonName == null) { amtacmactivation.acmCertErrors.push("Unable to get Intel AMT activation certificate common name."); continue; }
if (amtacmactivation.strictcommonname == true) {
// Use the certificate common name exactly
acmconfig.cn = certCommonName.value;
var certCommonNameSplit = certCommonName.value.split('.');
var topLevel = certCommonNameSplit[certCommonNameSplit.length - 1].toLowerCase();
var topLevelNum = TopLevelDomainExtendedSupport[topLevel];
if (topLevelNum != null) {
while (certCommonNameSplit.length > topLevelNum) { certCommonNameSplit.shift(); }
acmconfig.cn = certCommonNameSplit.join('.');
} else {
// Check if Intel AMT will allow some flexibility in the certificate common name
var certCommonNameSplit = certCommonName.value.split('.');
var topLevel = certCommonNameSplit[certCommonNameSplit.length - 1].toLowerCase();
var topLevelNum = TopLevelDomainExtendedSupport[topLevel];
if (topLevelNum != null) {
while (certCommonNameSplit.length > topLevelNum) { certCommonNameSplit.shift(); }
acmconfig.cn = certCommonNameSplit.join('.');
} else {
acmconfig.cn = certCommonName.value;
}
acmconfig.cn = certCommonName.value;
}
if(r.certs[0].md){
acmconfig.hashAlgorithm = r.certs[0].md.algorithm;
}
delete acmconfig.cert;
delete acmconfig.certpass;
acmconfig.certs = orderedCerts;
@ -636,16 +627,9 @@ module.exports.CertificateOperations = function (parent) {
};
// Return the SHA384 hash of the certificate public key
obj.getPublicKeyHashBinary = function (pem) {
const { X509Certificate } = require('crypto');
if (X509Certificate == null) {
// This version of NodeJS (<v15.6.0) does not support X509 certs, use Node-Forge instead which only supports RSA certs.
return obj.pki.getPublicKeyFingerprint(obj.pki.certificateFromPem(pem).publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() });
} else {
// This version of NodeJS supports x509 certificates
var cert = new X509Certificate(pem);
return obj.crypto.createHash('sha384').update(cert.publicKey.export({ type: ((cert.publicKey.asymmetricKeyType == 'rsa') ? 'pkcs1' : 'spki'), format: 'der' })).digest('binary');
}
obj.getPublicKeyHashBinary = function (cert) {
var publickey = obj.pki.certificateFromPem(cert).publicKey;
return obj.pki.getPublicKeyFingerprint(publickey, { encoding: 'binary', md: obj.forge.md.sha384.create() });
};
// Return the SHA384 hash of the certificate, return binary
@ -750,17 +734,14 @@ module.exports.CertificateOperations = function (parent) {
}
// Return true if the name is found in the certificates names, we support wildcard certificates
obj.compareCertificateNames = function (certNames, name) {
obj.compareCertificateNames = function(certNames, name) {
if (certNames == null) return false;
name = name.toLowerCase();
var xcertNames = [];
for (var i in certNames) { xcertNames.push(certNames[i].toLowerCase()); }
if (xcertNames.indexOf(name) >= 0) return true;
for (var i in xcertNames) {
if ((xcertNames[i].startsWith('*.') == true) && (name.endsWith(xcertNames[i].substring(1)) == true)) { return true; }
if (xcertNames[i].startsWith('http://*.') == true) {
if (name.endsWith(xcertNames[i].substring(8)) == true) { return true; }
if ((xcertNames[i].endsWith('/') == true) && (name.endsWith(xcertNames[i].substring(8, xcertNames[i].length - 1)) == true)) { return true; }
if (certNames.indexOf(name.toLowerCase()) >= 0) return true;
for (var i in certNames) {
if ((certNames[i].startsWith('*.') == true) && (name.endsWith(certNames[i].substring(1)) == true)) { return true; }
if (certNames[i].startsWith('http://*.') == true) {
if (name.endsWith(certNames[i].substring(8)) == true) { return true; }
if ((certNames[i].endsWith('/') == true) && (name.endsWith(certNames[i].substring(8, certNames[i].length - 1)) == true)) { return true; }
}
}
return false;
@ -768,102 +749,12 @@ module.exports.CertificateOperations = function (parent) {
// Return true if the certificate is valid
obj.checkCertificate = function (pem, key) {
const { X509Certificate } = require('crypto');
if (X509Certificate == null) {
// This version of NodeJS (<v15.6.0) does not support X509 certs, use Node-Forge instead which only supports RSA certs.
var cert = null;
try { cert = obj.pki.certificateFromPem(pem); } catch (ex) { return false; } // Unable to decode certificate
if (cert.serialNumber == '') return false; // Empty serial number is not allowed.
} else {
// This version of NodeJS supports x509 certificates
try {
const cert = new X509Certificate(pem);
if ((cert.serialNumber == '') || (cert.serialNumber == null)) return false; // Empty serial number is not allowed.
} catch (ex) { return false; } // Unable to decode certificate
}
var cert = null;
try { cert = obj.pki.certificateFromPem(pem); } catch (ex) { return false; } // Unable to decode certificate
if (cert.serialNumber == '') return false; // Empty serial number is not allowed.
return true;
}
// Get the Common Name from a certificate
obj.getCertificateCommonName = function (pem, field) {
if (field == null) { field = 'CN'; }
const { X509Certificate } = require('crypto');
if (X509Certificate == null) {
// This version of NodeJS (<v15.6.0) does not support X509 certs, use Node-Forge instead which only supports RSA certs.
var cert = obj.pki.certificateFromPem(pem);
if (cert.subject.getField(field) != null) return cert.subject.getField(field).value;
} else {
// This version of NodeJS supports x509 certificates
const subjects = new X509Certificate(pem).subject.split('\n');
for (var i in subjects) { if (subjects[i].startsWith(field + '=')) { return subjects[i].substring(field.length + 1); } }
}
return null;
}
// Get the Issuer Common Name from a certificate
obj.getCertificateIssuerCommonName = function (pem, field) {
if (field == null) { field = 'CN'; }
const { X509Certificate } = require('crypto');
if (X509Certificate == null) {
// This version of NodeJS (<v15.6.0) does not support X509 certs, use Node-Forge instead which only supports RSA certs.
var cert = obj.pki.certificateFromPem(pem);
if (cert.issuer.getField(field) != null) return cert.issuer.getField(field).value;
} else {
// This version of NodeJS supports x509 certificates
const subjects = new X509Certificate(pem).issuer.split('\n');
for (var i in subjects) { if (subjects[i].startsWith(field + '=')) { return subjects[i].substring(field.length + 1); } }
}
return null;
}
// Get the Common Name and alternate names from a certificate
obj.getCertificateAltNames = function (pem) {
const altNamesResults = [];
const { X509Certificate } = require('crypto');
if (X509Certificate == null) {
// This version of NodeJS (<v15.6.0) does not support X509 certs, use Node-Forge instead which only supports RSA certs.
var cert = obj.pki.certificateFromPem(pem);
if (cert.subject.getField('CN') != null) { altNamesResults.push(cert.subject.getField('CN').value); }
var altNames = cert.getExtension('subjectAltName');
if (altNames) {
for (i = 0; i < altNames.altNames.length; i++) {
if ((altNames.altNames[i] != null) && (altNames.altNames[i].type === 2) && (typeof altNames.altNames[i].value === 'string')) {
var acn = altNames.altNames[i].value.toLowerCase();
if (altNamesResults.indexOf(acn) == -1) { altNamesResults.push(acn); }
}
}
}
} else {
// This version of NodeJS supports x509 certificates
const cert = new X509Certificate(pem);
const subjects = cert.subject.split('\n');
for (var i in subjects) { if (subjects[i].startsWith('CN=')) { altNamesResults.push(subjects[i].substring(3)); } }
var subjectAltNames = cert.subjectAltName;
if (subjectAltNames != null) {
subjectAltNames = subjectAltNames.split(', ');
for (var i = 0; i < subjectAltNames.length; i++) {
if (subjectAltNames[i].startsWith('DNS:') && altNamesResults.indexOf(subjectAltNames[i].substring(4)) == -1) {
altNamesResults.push(subjectAltNames[i].substring(4));
}
}
}
}
return altNamesResults;
}
// Get the expiration time from a certificate
obj.getCertificateExpire = function (pem) {
const altNamesResults = [];
const { X509Certificate } = require('crypto');
if (X509Certificate == null) {
// This version of NodeJS (<v15.6.0) does not support X509 certs, use Node-Forge instead which only supports RSA certs.
return Date.parse(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.web.cert).validity.notAfter);
} else {
// This version of NodeJS supports x509 certificates
return Date.parse(new X509Certificate(pem).validTo);
}
}
// Decrypt private key if needed
obj.decryptPrivateKey = function (key) {
if (typeof key != 'string') return key;
@ -918,14 +809,12 @@ module.exports.CertificateOperations = function (parent) {
var xext = xroot.getExtension('keyUsage');
if ((xext == null) || (xext.keyCertSign !== true) || (xroot.serialNumber == '')) {
// We need to fix this certificate
parent.common.moveOldFiles(['root-cert-public-backup.crt']);
obj.fs.writeFileSync(parent.getConfigFilePath('root-cert-public-backup.crt'), rootCertificate);
if (xroot.serialNumber == '') { console.log("Fixing root certificate to add serial number..."); xroot.serialNumber = '' + require('crypto').randomBytes(4).readUInt32BE(0); }
if ((xext == null) || (xext.keyCertSign !== true)) { console.log("Fixing root certificate to add signing key usage..."); xroot.setExtensions([{ name: 'basicConstraints', cA: true }, { name: 'subjectKeyIdentifier' }, { name: 'keyUsage', keyCertSign: true }]); }
var xrootPrivateKey = obj.pki.privateKeyFromPem(rootPrivateKey);
xroot.sign(xrootPrivateKey, obj.forge.md.sha384.create());
r.root.cert = obj.pki.certificateToPem(xroot);
parent.common.moveOldFiles([parent.getConfigFilePath('root-cert-public.crt')]);
try { obj.fs.writeFileSync(parent.getConfigFilePath('root-cert-public.crt'), r.root.cert); } catch (ex) { }
}
}
@ -966,9 +855,6 @@ module.exports.CertificateOperations = function (parent) {
if (obj.fileExists("codesign-cert-public.crt") && obj.fileExists("codesign-cert-private.key")) {
r.codesign = { cert: obj.fileLoad("codesign-cert-public.crt", 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad("codesign-cert-private.key", 'utf8')) };
if (obj.checkCertificate(r.codesign.cert, r.codesign.key) == false) { delete r.codesign; } else { rcount++; }
} else {
// If we are reading certificates from a database or vault and are just missing the code signing cert, skip it.
if (parent.configurationFiles != null) { rcount++; }
}
// If the swarm server certificate exist, load it (This is an optional certificate)
@ -1020,23 +906,32 @@ module.exports.CertificateOperations = function (parent) {
if (rcount === rcountmax) {
// Fetch the certificates names for the main certificate
r.AmtMpsName = obj.getCertificateCommonName(r.mps.cert);
r.WebIssuer = obj.getCertificateIssuerCommonName(r.web.cert);
r.CommonName = obj.getCertificateCommonName(r.web.cert);
r.CommonNames = obj.getCertificateAltNames(r.web.cert);
r.RootName = obj.getCertificateCommonName(r.root.cert);
// If the "cert" name is not set, try to use the certificate CN instead (ok if the certificate is not wildcard).
if (commonName == 'un-configured') {
if (r.CommonName.startsWith('*.')) { console.log("ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate."); process.exit(0); return; }
commonName = r.CommonName;
r.AmtMpsName = obj.pki.certificateFromPem(r.mps.cert).subject.getField('CN').value;
var webCertificate = obj.pki.certificateFromPem(r.web.cert);
r.WebIssuer = webCertificate.issuer.getField('CN').value;
if (commonName == 'un-configured') { // If the "cert" name is not set, try to use the certificate CN instead (ok if the certificate is not wildcard).
commonName = webCertificate.subject.getField('CN').value;
if (commonName.startsWith('*.')) { console.log("ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate."); process.exit(0); return; }
}
r.CommonName = commonName;
r.CommonNames = [commonName.toLowerCase()];
var altNames = webCertificate.getExtension('subjectAltName');
if (altNames) {
for (i = 0; i < altNames.altNames.length; i++) {
if ((altNames.altNames[i] != null) && (altNames.altNames[i].type === 2) && (typeof altNames.altNames[i].value === 'string')) {
var acn = altNames.altNames[i].value.toLowerCase();
if (r.CommonNames.indexOf(acn) == -1) { r.CommonNames.push(acn); }
}
}
}
var rootCertificate = obj.pki.certificateFromPem(r.root.cert);
r.RootName = rootCertificate.subject.getField('CN').value;
}
// Look for domains that have DNS names and load their certificates
r.dns = {};
for (i in config.domains) {
if ((i != '') && (config.domains[i] != null) && (config.domains[i].dns != null)) {
if ((i != "") && (config.domains[i] != null) && (config.domains[i].dns != null)) {
dnsname = config.domains[i].dns;
// Check if this domain matches a parent wildcard cert, if so, use the parent cert.
if (obj.compareCertificateNames(r.CommonNames, dnsname) == true) {
@ -1049,7 +944,6 @@ module.exports.CertificateOperations = function (parent) {
config.domains[i].certs = r.dns[i];
} else {
console.log("WARNING: File \"webserver-" + i + "-cert-public.crt\" missing, domain \"" + i + "\" will not work correctly.");
rcountmax++;
}
} else {
// If the web certificate already exist, load it. Load both certificate and private key
@ -1075,35 +969,25 @@ module.exports.CertificateOperations = function (parent) {
}
}
// If we have all the certificates we need, stop here.
if (rcount === rcountmax) {
if ((certargs == null) && (mpscertargs == null)) { if (func != undefined) { func(r); } return r; } // If no certificate arguments are given, keep the certificate
const xcountry = obj.getCertificateCommonName(r.web.cert, 'C');
const xorganization = obj.getCertificateCommonName(r.web.cert, 'O');
var xcountry, xcountryField = webCertificate.subject.getField('C');
if (xcountryField != null) { xcountry = xcountryField.value; }
var xorganization, xorganizationField = webCertificate.subject.getField('O');
if (xorganizationField != null) { xorganization = xorganizationField.value; }
if (certargs == null) { commonName = r.CommonName; country = xcountry; organization = xorganization; }
// Check if we have correct certificates.
if (obj.compareCertificateNames(r.CommonNames, commonName) == false) { console.log("Error: " + commonName + " does not match name in TLS certificate: " + r.CommonNames.join(', ')); forceWebCertGen = 1; } else { r.CommonName = commonName; }
// Check if we have correct certificates
if (obj.compareCertificateNames(r.CommonNames, commonName) == false) { forceWebCertGen = 1; }
if (r.AmtMpsName != mpsCommonName) { forceMpsCertGen = 1; }
if (args.keepcerts == true) { forceWebCertGen = 0; forceMpsCertGen = 0; r.CommonName = commonName; }
// If the certificates matches what we want, use them.
if ((forceWebCertGen == 0) && (forceMpsCertGen == 0)) {
if (func !== null) { func(r); }
if (func !== undefined) { func(r); }
return r;
}
}
if (parent.configurationFiles != null) {
console.log("Error: Vault/Database missing some certificates.");
if (r.root == null) { console.log(' Code signing certificate is missing.'); }
if (r.web == null) { console.log(' HTTPS web certificate is missing.'); }
if (r.mps == null) { console.log(' Intel AMT MPS certificate is missing.'); }
if (r.agent == null) { console.log(' Server agent authentication certificate is missing.'); }
if (r.codesign == null) { console.log(' Agent code signing certificate is missing.'); }
process.exit(0);
return null;
}
if (parent.configurationFiles != null) { console.log("Error: Vault/Database missing some certificates."); process.exit(0); return null; }
console.log("Generating certificates, may take a few minutes...");
parent.updateServerState('state', 'generatingcertificates');
@ -1122,16 +1006,9 @@ module.exports.CertificateOperations = function (parent) {
if (r.root == null) {
// If the root certificate does not exist, create one
console.log("Generating root certificate...");
if (typeof args.rootcertcommonname == 'string') {
// If a root certificate common name is specified, use it.
rootCertAndKey = obj.GenerateRootCertificate(false, args.rootcertcommonname, null, null, strongCertificate);
} else {
// A root certificate common name is not specified, use the default one.
rootCertAndKey = obj.GenerateRootCertificate(true, 'MeshCentralRoot', null, null, strongCertificate);
}
rootCertAndKey = obj.GenerateRootCertificate(true, 'MeshCentralRoot', null, null, strongCertificate);
rootCertificate = obj.pki.certificateToPem(rootCertAndKey.cert);
rootPrivateKey = obj.pki.privateKeyToPem(rootCertAndKey.key);
parent.common.moveOldFiles([parent.getConfigFilePath('root-cert-public.crt'), parent.getConfigFilePath('root-cert-private.key')]);
obj.fs.writeFileSync(parent.getConfigFilePath('root-cert-public.crt'), rootCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath('root-cert-private.key'), rootPrivateKey);
} else {
@ -1144,12 +1021,11 @@ module.exports.CertificateOperations = function (parent) {
// If the web certificate does not exist, create one
var webCertAndKey, webCertificate, webPrivateKey;
if ((r.web == null) || (forceWebCertGen === 1)) {
if ((r.web == null) || (forceWebCertGen == 1)) {
console.log("Generating HTTPS certificate...");
webCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization, null, strongCertificate);
webCertificate = obj.pki.certificateToPem(webCertAndKey.cert);
webPrivateKey = obj.pki.privateKeyToPem(webCertAndKey.key);
parent.common.moveOldFiles([parent.getConfigFilePath('webserver-cert-public.crt'), parent.getConfigFilePath('webserver-cert-private.key')]);
obj.fs.writeFileSync(parent.getConfigFilePath('webserver-cert-public.crt'), webCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath('webserver-cert-private.key'), webPrivateKey);
} else {
@ -1163,8 +1039,7 @@ module.exports.CertificateOperations = function (parent) {
webPrivateKey = r.web.key;
}
}
var webIssuer = null;
if (webCertAndKey.cert.issuer.getField('CN') != null) { webIssuer = webCertAndKey.cert.issuer.getField('CN').value; }
var webIssuer = webCertAndKey.cert.issuer.getField('CN').value;
// If the mesh agent server certificate does not exist, create one
var agentCertAndKey, agentCertificate, agentPrivateKey;
@ -1173,7 +1048,6 @@ module.exports.CertificateOperations = function (parent) {
agentCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, true, 'MeshCentralAgentServer', country, organization, { }, strongCertificate);
agentCertificate = obj.pki.certificateToPem(agentCertAndKey.cert);
agentPrivateKey = obj.pki.privateKeyToPem(agentCertAndKey.key);
parent.common.moveOldFiles([parent.getConfigFilePath('agentserver-cert-public.crt'), parent.getConfigFilePath('agentserver-cert-private.key')]);
obj.fs.writeFileSync(parent.getConfigFilePath('agentserver-cert-public.crt'), agentCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath('agentserver-cert-private.key'), agentPrivateKey);
} else {
@ -1190,7 +1064,6 @@ module.exports.CertificateOperations = function (parent) {
codesignCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, true, commonName, country, organization, { codeSign: true }, strongCertificate);
codesignCertificate = obj.pki.certificateToPem(codesignCertAndKey.cert);
codesignPrivateKey = obj.pki.privateKeyToPem(codesignCertAndKey.key);
parent.common.moveOldFiles([parent.getConfigFilePath('codesign-cert-public.crt'), parent.getConfigFilePath('codesign-cert-private.key')]);
obj.fs.writeFileSync(parent.getConfigFilePath('codesign-cert-public.crt'), codesignCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath('codesign-cert-private.key'), codesignPrivateKey);
} else {
@ -1202,12 +1075,11 @@ module.exports.CertificateOperations = function (parent) {
// If the Intel AMT MPS certificate does not exist, create one
var mpsCertAndKey, mpsCertificate, mpsPrivateKey;
if ((r.mps == null) || (forceMpsCertGen === 1)) {
if ((r.mps == null) || (forceMpsCertGen == 1)) {
console.log("Generating Intel AMT MPS certificate...");
mpsCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, mpsCommonName, mpsCountry, mpsOrganization, null, false);
mpsCertificate = obj.pki.certificateToPem(mpsCertAndKey.cert);
mpsPrivateKey = obj.pki.privateKeyToPem(mpsCertAndKey.key);
parent.common.moveOldFiles([parent.getConfigFilePath('mpsserver-cert-public.crt'), parent.getConfigFilePath('mpsserver-cert-private.key')]);
obj.fs.writeFileSync(parent.getConfigFilePath('mpsserver-cert-public.crt'), mpsCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath('mpsserver-cert-private.key'), mpsPrivateKey);
} else {
@ -1221,7 +1093,7 @@ module.exports.CertificateOperations = function (parent) {
// Fetch the certificates names for the main certificate
var webCertificate = obj.pki.certificateFromPem(r.web.cert);
if (webCertificate.issuer.getField('CN') != null) { r.WebIssuer = webCertificate.issuer.getField('CN').value; } else { r.WebIssuer = null; }
r.WebIssuer = webCertificate.issuer.getField('CN').value;
r.CommonName = webCertificate.subject.getField('CN').value;
if (r.CommonName.startsWith('*.')) {
if (commonName.indexOf('.') == -1) { console.log("ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate."); process.exit(0); return; }
@ -1243,7 +1115,7 @@ module.exports.CertificateOperations = function (parent) {
// Look for domains with DNS names that have no certificates and generated them.
for (i in config.domains) {
if ((i != '') && (config.domains[i] != null) && (config.domains[i].dns != null)) {
if ((i != "") && (config.domains[i] != null) && (config.domains[i].dns != null)) {
dnsname = config.domains[i].dns;
// Check if this domain matches a parent wildcard cert, if so, use the parent cert.
if (obj.compareCertificateNames(r.CommonNames, dnsname) == true) {
@ -1256,7 +1128,6 @@ module.exports.CertificateOperations = function (parent) {
var xwebCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, dnsname, country, organization, null, strongCertificate);
var xwebCertificate = obj.pki.certificateToPem(xwebCertAndKey.cert);
var xwebPrivateKey = obj.pki.privateKeyToPem(xwebCertAndKey.key);
parent.common.moveOldFiles([ parent.getConfigFilePath('webserver-' + i + '-cert-public.crt'), parent.getConfigFilePath('webserver-' + i + '-cert-private.key') ]);
obj.fs.writeFileSync(parent.getConfigFilePath('webserver-' + i + '-cert-public.crt'), xwebCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath('webserver-' + i + '-cert-private.key'), xwebPrivateKey);
r.dns[i] = { cert: xwebCertificate, key: xwebPrivateKey };
@ -1408,15 +1279,20 @@ module.exports.CertificateOperations = function (parent) {
// Perform any general operation
obj.acceleratorPerformOperation = function (operation, data, tag, func) {
var acc = obj.getAccelerator();
if (acc == null) {
// Add to pending accelerator workload
acceleratorPerformSignaturePushFuncCall++;
pendingAccelerator.push({ action: operation, data: data, tag: tag, func: func });
if (acceleratorTotalCount <= 1) {
// No accelerators available
require(program).processMessage({ action: operation, data: data, tag: tag, func: func });
} else {
// Send to accelerator now
acceleratorPerformSignatureRunFuncCall++;
acc.send(acc.x = { action: operation, data: data, tag: tag, func: func });
var acc = obj.getAccelerator();
if (acc == null) {
// Add to pending accelerator workload
acceleratorPerformSignaturePushFuncCall++;
pendingAccelerator.push({ action: operation, data: data, tag: tag, func: func });
} else {
// Send to accelerator now
acceleratorPerformSignatureRunFuncCall++;
acc.send(acc.x = { action: operation, data: data, tag: tag, func: func });
}
}
};

101
common.js
View file

@ -106,7 +106,7 @@ module.exports.random = function (max) {
return r;
};
// Split a comma separated string, ignoring commas in quotes.
// Split a comma seperated string, ignoring commas in quotes.
module.exports.quoteSplit = function (str) {
var tmp = '', quote = 0, result = [];
for (var i in str) { if (str[i] == '"') { quote = (quote + 1) % 2; } if ((str[i] == ',') && (quote == 0)) { tmp = tmp.trim(); result.push(tmp); tmp = ''; } else { tmp += str[i]; } }
@ -142,48 +142,25 @@ module.exports.zeroPad = function(num, c) { if (c == null) { c = 2; } var s = '0
// Lowercase all the names in a object recursively
// Allow for exception keys, child of exceptions will not get lower-cased.
// Exceptions is an array of "keyname" or "parent\keyname"
module.exports.objKeysToLower = function (obj, exceptions, parent) {
module.exports.objKeysToLower = function (obj, exceptions) {
for (var i in obj) {
if ((typeof obj[i] == 'object') &&
((exceptions == null) || (exceptions.indexOf(i.toLowerCase()) == -1) && ((parent == null) || (exceptions.indexOf(parent.toLowerCase() + '/' + i.toLowerCase()) == -1)))
) {
module.exports.objKeysToLower(obj[i], exceptions, i); // LowerCase all key names in the child object
}
if ((typeof obj[i] == 'object') && ((exceptions == null) || (exceptions.indexOf(i.toLowerCase()) == -1))) { module.exports.objKeysToLower(obj[i], exceptions); } // LowerCase all key names in the child object
if (i.toLowerCase() !== i) { obj[i.toLowerCase()] = obj[i]; delete obj[i]; } // LowerCase all key names
}
return obj;
};
// Escape and unescape field names so there are no invalid characters for MongoDB/NeDB ("$", ",", ".", see https://github.com/seald/nedb/tree/master?tab=readme-ov-file#inserting-documents)
module.exports.escapeFieldName = function (name) { if ((name.indexOf(',') == -1) && (name.indexOf('%') == -1) && (name.indexOf('.') == -1) && (name.indexOf('$') == -1)) return name; return name.split('%').join('%25').split('.').join('%2E').split('$').join('%24').split(',').join('%2C'); };
module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2C').join(',').split('%2E').join('.').split('%24').join('$').split('%25').join('%'); };
// Escape and unexcape feild names so there are no invalid characters for MongoDB
module.exports.escapeFieldName = function (name) { if ((name.indexOf('%') == -1) && (name.indexOf('.') == -1) && (name.indexOf('$') == -1)) return name; return name.split('%').join('%25').split('.').join('%2E').split('$').join('%24'); };
module.exports.unEscapeFieldName = function (name) { if (name.indexOf('%') == -1) return name; return name.split('%2E').join('.').split('%24').join('$').split('%25').join('%'); };
// Escape all links, SSH and RDP usernames
// This is required for databases like NeDB that don't accept "." or "," as part of a field name.
module.exports.escapeLinksFieldNameEx = function (docx) { if ((docx.links == null) && (docx.ssh == null) && (docx.rdp == null)) { return docx; } return module.exports.escapeLinksFieldName(docx); };
module.exports.escapeLinksFieldName = function (docx) {
var doc = Object.assign({}, docx);
if (doc.links != null) { doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } }
if (doc.ssh != null) { doc.ssh = Object.assign({}, doc.ssh); for (var i in doc.ssh) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.ssh[ue] = doc.ssh[i]; delete doc.ssh[i]; } } }
if (doc.rdp != null) { doc.rdp = Object.assign({}, doc.rdp); for (var i in doc.rdp) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.rdp[ue] = doc.rdp[i]; delete doc.rdp[i]; } } }
return doc;
};
module.exports.unEscapeLinksFieldName = function (doc) {
if (doc.links != null) { for (var j in doc.links) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.links[ue] = doc.links[j]; delete doc.links[j]; } } }
if (doc.ssh != null) { for (var j in doc.ssh) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.ssh[ue] = doc.ssh[j]; delete doc.ssh[j]; } } }
if (doc.rdp != null) { for (var j in doc.rdp) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.rdp[ue] = doc.rdp[j]; delete doc.rdp[j]; } } }
return doc;
};
// Escape all links
module.exports.escapeLinksFieldNameEx = function (docx) { if (docx.links == null) { return docx; } var doc = Object.assign({}, docx); doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } return doc; };
module.exports.escapeLinksFieldName = function (docx) { var doc = Object.assign({}, docx); if (doc.links != null) { doc.links = Object.assign({}, doc.links); for (var i in doc.links) { var ue = module.exports.escapeFieldName(i); if (ue !== i) { doc.links[ue] = doc.links[i]; delete doc.links[i]; } } } return doc; };
module.exports.unEscapeLinksFieldName = function (doc) { if (doc.links != null) { for (var j in doc.links) { var ue = module.exports.unEscapeFieldName(j); if (ue !== j) { doc.links[ue] = doc.links[j]; delete doc.links[j]; } } } return doc; };
//module.exports.escapeAllLinksFieldName = function (docs) { for (var i in docs) { module.exports.escapeLinksFieldName(docs[i]); } return docs; };
module.exports.unEscapeAllLinksFieldName = function (docs) { for (var i in docs) { docs[i] = module.exports.unEscapeLinksFieldName(docs[i]); } return docs; };
// Escape field names for aceBase
var aceEscFields = ['links', 'ssh', 'rdp', 'notify'];
module.exports.aceEscapeFieldNames = function (docx) { var doc = Object.assign({}, docx); for (var k in aceEscFields) { if (typeof doc[aceEscFields[k]] == 'object') { doc[aceEscFields[k]] = Object.assign({}, doc[aceEscFields[k]]); for (var i in doc[aceEscFields[k]]) { var ue = encodeURIComponent(i); if (ue !== i) { doc[aceEscFields[k]][ue] = doc[aceEscFields[k]][i]; delete doc[aceEscFields[k]][i]; } } } } return doc; };
module.exports.aceUnEscapeFieldNames = function (doc) { for (var k in aceEscFields) { if (typeof doc[aceEscFields[k]] == 'object') { for (var j in doc[aceEscFields[k]]) { var ue = decodeURIComponent(j); if (ue !== j) { doc[aceEscFields[k]][ue] = doc[aceEscFields[k]][j]; delete doc[aceEscFields[k]][j]; } } } } return doc; };
module.exports.aceUnEscapeAllFieldNames = function (docs) { for (var i in docs) { docs[i] = module.exports.aceUnEscapeFieldNames(docs[i]); } return docs; };
// Validation methods
module.exports.validateString = function (str, minlen, maxlen) { return ((str != null) && (typeof str == 'string') && ((minlen == null) || (str.length >= minlen)) && ((maxlen == null) || (str.length <= maxlen))); };
module.exports.validateInt = function (int, minval, maxval) { return ((int != null) && (typeof int == 'number') && ((minval == null) || (int >= minval)) && ((maxval == null) || (int <= maxval))); };
@ -334,11 +311,6 @@ module.exports.meshServerRightsArrayToNumber = function (val) {
if (r == 'locked') { newAccRights |= 32; }
if (r == 'nonewgroups') { newAccRights |= 64; }
if (r == 'notools') { newAccRights |= 128; }
if (r == 'usergroups') { newAccRights |= 256; }
if (r == 'recordings') { newAccRights |= 512; }
if (r == 'locksettings') { newAccRights |= 1024; }
if (r == 'allevents') { newAccRights |= 2048; }
if (r == 'nonewdevices') { newAccRights |= 4096; }
}
return newAccRights;
}
@ -367,56 +339,3 @@ function validateObjectForMongoRec(obj, maxStrLen) {
}
return true;
}
// Parse a version string of the type n.n.n.n
module.exports.parseVersion = function (verstr) {
if (typeof verstr != 'string') return null;
const r = [], verstrsplit = verstr.split('.');
if (verstrsplit.length != 4) return null;
for (var i in verstrsplit) {
var n = parseInt(verstrsplit[i]);
if (isNaN(n) || (n < 0) || (n > 65535)) return null;
r.push(n);
}
return r;
}
// Move old files. If we are about to overwrite a file, we can move if first just in case the change needs to be reverted
module.exports.moveOldFiles = function (filelist) {
// Fine an old extension that works for all files in the file list
var oldFileExt, oldFileExtCount = 0, extOk;
do {
extOk = true;
if (++oldFileExtCount == 1) { oldFileExt = '-old'; } else { oldFileExt = '-old' + oldFileExtCount; }
for (var i in filelist) { if (fs.existsSync(filelist[i] + oldFileExt) == true) { extOk = false; } }
} while (extOk == false);
for (var i in filelist) { try { fs.renameSync(filelist[i], filelist[i] + oldFileExt); } catch (ex) { } }
}
// Convert strArray to Array, returns array if strArray or null if any other type
module.exports.convertStrArray = function (object, split) {
if (split && typeof object === 'string') {
return object.split(split)
} else if (typeof object === 'string') {
return Array(object);
} else if (Array.isArray(object)) {
return object
} else {
return []
}
}
module.exports.uniqueArray = function (a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}

View file

@ -1,141 +0,0 @@
module.exports.CreateCrowdSecBouncer = function (parent, config) {
const obj = {};
// 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');
// Current captcha state
const currentCaptchaIpList = {};
// Set the default values. "config" will come in with lowercase names with everything, so we need to correct some value names.
if (typeof config.useragent != 'string') { config.useragent = 'CrowdSec Express-NodeJS bouncer/v0.0.1'; }
if (typeof config.timeout != 'number') { config.timeout = 2000; }
if ((typeof config.fallbackremediation != 'string') || (['bypass', 'captcha', 'ban'].indexOf(config.fallbackremediation) == -1)) { config.fallbackremediation = BAN_REMEDIATION; }
if (typeof config.maxremediation != 'number') { config.maxremediation = BAN_REMEDIATION; }
if (typeof config.captchagenerationcacheduration != 'number') { config.captchagenerationcacheduration = 60 * 1000; } // 60 seconds
if (typeof config.captcharesolutioncacheduration != 'number') { config.captcharesolutioncacheduration = 30 * 60 * 1000; } // 30 minutes
if (typeof config.captchatexts != 'object') { config.captchatexts = {}; } else {
if (typeof config.captchatexts.tabtitle == 'string') { config.captchatexts.tabTitle = config.captchatexts.tabtitle; delete config.captchatexts.tabtitle; } // Fix "tabTitle" capitalization
}
if (typeof config.bantexts != 'object') { config.bantexts = {}; } else {
if (typeof config.bantexts.tabtitle == 'string') { config.bantexts.tabTitle = config.bantexts.tabtitle; delete config.bantexts.tabtitle; } // Fix "tabTitle" capitalization
}
if (typeof config.colors != 'object') { config.colors = {}; } else {
var colors = {};
// All of the values in "text" and "background" sections happen to be lowercase, so, we can use the values as-is.
if (typeof config.colors.text == 'object') { colors.text = config.colors.text; }
if (typeof config.colors.background == 'object') { colors.background = config.colors.background; }
config.colors = colors;
}
if (typeof config.hidecrowdsecmentions != 'boolean') { config.hidecrowdsecmentions = false; }
if (typeof config.customcss != 'string') { delete config.customcss; }
if (typeof config.bypass != 'boolean') { config.bypass = false; }
if (typeof config.customlogger != 'object') { delete config.customlogger; }
if (typeof config.bypassconnectiontest != 'boolean') { config.bypassconnectiontest = false; }
// Setup the logger
var logger = config.customLogger ? config.customLogger : getLogger();
// Configure the bouncer
configure({
url: config.url,
apiKey: config.apikey,
userAgent: config.useragent,
timeout: config.timeout,
fallbackRemediation: config.fallbackremediation,
maxRemediation: config.maxremediation,
captchaTexts: config.captchatexts,
banTexts: config.bantexts,
colors: config.colors,
hideCrowdsecMentions: config.hidecrowdsecmentions,
customCss: config.customcss
});
// Test connectivity
obj.testConnectivity = async function() { return (await testConnectionToCrowdSec())['success']; }
// Process a web request
obj.process = async function (domain, req, res, next) {
try {
var remediation = config.fallbackremediation;
try { remediation = await getRemediationForIp(req.clientIp); } catch (ex) { }
//console.log('CrowdSec', req.clientIp, remediation, req.url);
switch (remediation) {
case BAN_REMEDIATION:
const banWallTemplate = await renderBanWall();
res.status(403);
res.send(banWallTemplate);
return true;
case CAPTCHA_REMEDIATION:
if ((currentCaptchaIpList[req.clientIp] == null) || (currentCaptchaIpList[req.clientIp].resolved !== true)) {
var domainCaptchaUrl = ((domain != null) && (domain.id != '') && (domain.dns == null)) ? ('/' + domain.id + '/captcha.ashx') : '/captcha.ashx';
if (req.url != domainCaptchaUrl) { res.redirect(domainCaptchaUrl); return true; }
}
break;
}
} catch (ex) { }
return false;
}
// Process a captcha request
obj.applyCaptcha = async function (req, res, next) {
await applyCaptchaEx(req.clientIp, req, res, next, config.captchagenerationcacheduration, config.captcharesolutioncacheduration, logger);
}
// Process a captcha request
async function applyCaptchaEx(ip, req, res, next, captchaGenerationCacheDuration, captchaResolutionCacheDuration, loggerInstance) {
logger = loggerInstance;
let error = false;
if (currentCaptchaIpList[ip] == null) {
generateCaptcha(ip, captchaGenerationCacheDuration);
} else {
if (currentCaptchaIpList[ip] && currentCaptchaIpList[ip].resolved) {
logger.debug({ type: 'CAPTCHA_ALREADY_SOLVED', ip });
next();
return;
} else {
if (req.body && req.body.crowdsec_captcha) {
if (req.body.refresh === '1') { generateCaptcha(ip, captchaGenerationCacheDuration); }
if (req.body.phrase !== '') {
if (currentCaptchaIpList[ip].text === req.body.phrase) {
currentCaptchaIpList[ip].resolved = true;
setTimeout(function() { if (currentCaptchaIpList[ip]) { delete currentCaptchaIpList[ip]; } }, captchaResolutionCacheDuration);
res.redirect(req.originalUrl);
logger.info({ type: 'CAPTCHA_RESOLUTION', ip, result: true });
return;
} else {
logger.info({ type: 'CAPTCHA_RESOLUTION', ip, result: false });
error = true;
}
}
}
}
}
const captchaWallTemplate = await renderCaptchaWall({ captchaImageTag: currentCaptchaIpList[ip].data, captchaResolutionFormUrl: '', error });
res.status(401);
res.send(captchaWallTemplate);
};
// Generate a CAPTCHA
function generateCaptcha(ip, captchaGenerationCacheDuration) {
const captcha = svgCaptcha.create();
currentCaptchaIpList[ip] = {
data: captcha.data,
text: captcha.text,
resolved: false,
};
setTimeout(() => {
if (currentCaptchaIpList[ip]) { delete currentCaptchaIpList[ip]; }
}, captchaGenerationCacheDuration);
logger.debug({ type: "GENERATE_CAPTCHA", ip });
};
return obj;
}

2548
db.js

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,15 @@
"@seald-io/nedb": "4.0.4",
"archiver": "7.0.1",
"body-parser": "1.20.3",
"cbor": "5.2.0",
"compression": "1.7.5",
"cookie-session": "2.1.0",
"express": "4.21.2",
"express-handlebars": "7.1.3",
"express-ws": "5.0.2",
"ipcheck": "0.1.0",
"minimist": "1.2.8",
"multiparty": "4.2.3",
"node-forge": "1.3.1",
"ua-parser-js": "1.0.39",
"ws": "8.18.0",
"yauzl": "2.10.0"
"archiver": "^5.3.1",
"body-parser": "^1.19.0",
"cbor": "~5.2.0",
"compression": "^1.7.4",
"cookie-session": "^1.4.0",
"express": "^4.17.0",
"express-handlebars": "^5.3.5",
"express-ws": "^4.0.0",
"ipcheck": "^0.1.0",
"minimist": "^1.2.5",
"multiparty": "^4.2.1",
"@yetzt/nedb": "^1.8.0",
"node-forge": "^1.0.0",
"ws": "^5.2.3",
"yauzl": "^2.10.0"

View file

@ -1,29 +0,0 @@
# How to create a docker image for meshcentral
```
> git clone https://github.com/Ylianst/MeshCentral.git
> cd MeshCentral
> docker build -f docker/Dockerfile --force-rm -t meshcentral .
# alternative, if you want to include the mongodb-tools (mongodump, ...), you can add the 'INCLUDE_MONGODBTOOLS=yes' build argument
> docker build -f docker/Dockerfile --force-rm --build-arg INCLUDE_MONGODBTOOLS=yes -t meshcentral .
# (optional) cleanup after docker build:
> cd ..
> rm -rf MeshCentral/
```
> | Argument | Description |
> | :--- | :--- |
> | -f docker/Dockerfile | Path/Name of the Dockerfile |
> | --force-rm | Always remove intermediate containers |
> | -t meshcentral | Name and optionally a tag in the 'name:tag' format |
### Optional build arguments
> | Argument | Description |
> | :--- | :--- |
> | INCLUDE_MONGODBTOOLS=yes | Includes mongodb-tools (mongodump, ...) in the image |
> | DISABLE_MINIFY=yes | Disables the minification of files |
> | DISABLE_TRANSLATE=yes | Disables the translation of files |

View file

@ -1,94 +1,66 @@
FROM --platform=$BUILDPLATFORM node:22-alpine AS builder
FROM node:current-alpine AS base
RUN mkdir -p /opt/meshcentral/meshcentral
COPY ./ /opt/meshcentral/meshcentral/
#Add non-root user, add installation directories and assign proper permissions
RUN mkdir -p /opt/meshcentral
# meshcentral installation
WORKDIR /opt/meshcentral
RUN apk add --no-cache bash
FROM base AS builder
ARG DISABLE_MINIFY=""
ARG DISABLE_TRANSLATE=""
RUN mkdir /opt/meshcentral/meshcentral
COPY ./ /opt/meshcentral/meshcentral/
RUN if ! [ -z "$DISABLE_MINIFY" ] && [ "$DISABLE_MINIFY" != "yes" ] && [ "$DISABLE_MINIFY" != "YES" ] \
&& [ "$DISABLE_MINIFY" != "true" ] && [ "$DISABLE_MINIFY" != "TRUE" ]; then \
echo -e "\e[0;31;49mInvalid value for build argument DISABLE_MINIFY, possible values: yes/true\e[;0m"; exit 1; \
echo -e "\e[0;31;49mInvalid value for build argument DISABLE_MINIFY, possible values: yes/true\e[;0m"; exit 1; \
fi
RUN if ! [ -z "$DISABLE_TRANSLATE" ] && [ "$DISABLE_TRANSLATE" != "yes" ] && [ "$DISABLE_TRANSLATE" != "YES" ] \
&& [ "$DISABLE_TRANSLATE" != "true" ] && [ "$DISABLE_TRANSLATE" != "TRUE" ]; then \
echo -e "\e[0;31;49mInvalid value for build argument DISABLE_TRANSLATE, possible values: yes/true\e[;0m"; exit 1; \
echo -e "\e[0;31;49mInvalid value for build argument DISABLE_TRANSLATE, possible values: yes/true\e[;0m"; exit 1; \
fi
# install translate/minify modules if need too
RUN if [ -z "$DISABLE_MINIFY" ] || [ -z "$DISABLE_TRANSLATE" ]; then cd meshcentral && npm install html-minifier@4.0.0 jsdom@22.1.0 esprima@4.0.1; fi
# first extractall if need too
RUN if [ -z "$DISABLE_MINIFY" ] || [ -z "$DISABLE_TRANSLATE" ]; then cd meshcentral/translate && node translate.js extractall; fi
# first try throws Error: Cannot find module 'jsdom'
RUN if [ -z "$DISABLE_MINIFY" ] || [ -z "$DISABLE_TRANSLATE" ]; then cd meshcentral/translate && node translate.js extractall; exit 0; fi
# minify files
RUN if [ -z "$DISABLE_MINIFY" ]; then cd meshcentral/translate && node translate.js minifyall; fi
# translate
RUN if [ -z "$DISABLE_TRANSLATE" ]; then cd meshcentral/translate && node translate.js extractall; fi
RUN if [ -z "$DISABLE_TRANSLATE" ]; then cd meshcentral/translate && node translate.js translateall; fi
# cleanup
RUN rm -rf /opt/meshcentral/meshcentral/docker
RUN rm -rf /opt/meshcentral/meshcentral/node_modules
FROM --platform=$TARGETPLATFORM alpine:3.21
#Add non-root user, add installation directories and assign proper permissions
RUN mkdir -p /opt/meshcentral/meshcentral
# meshcentral installation
WORKDIR /opt/meshcentral
RUN apk update \
&& apk add --no-cache --update tzdata nodejs npm bash python3 make gcc g++ \
&& rm -rf /var/cache/apk/*
RUN npm install -g npm@latest
FROM base
ARG INCLUDE_MONGODBTOOLS=""
ARG PREINSTALL_LIBS="false"
# environment variables
ENV NODE_ENV="production"
ENV CONFIG_FILE="config.json"
# environment variables for initial configuration file
ENV USE_MONGODB="false"
ENV MONGO_INITDB_ROOT_USERNAME="root"
ENV MONGO_INITDB_ROOT_PASSWORD="pass"
ENV MONGO_URL=""
ENV HOSTNAME="localhost"
ENV ALLOW_NEW_ACCOUNTS="true"
ENV ALLOWPLUGINS="false"
ENV LOCALSESSIONRECORDING="true"
ENV MINIFY="false"
ENV WEBRTC="false"
ENV IFRAME="false"
ENV SESSION_KEY=""
ENV REVERSE_PROXY="false"
ENV REVERSE_PROXY_TLS_PORT=""
ENV ARGS=""
RUN if ! [ -z "$INCLUDE_MONGODBTOOLS" ] && [ "$INCLUDE_MONGODBTOOLS" != "yes" ] && [ "$INCLUDE_MONGODBTOOLS" != "YES" ] \
&& [ "$INCLUDE_MONGODBTOOLS" != "true" ] && [ "$INCLUDE_MONGODBTOOLS" != "TRUE" ]; then \
echo -e "\e[0;31;49mInvalid value for build argument INCLUDE_MONGODBTOOLS, possible values: yes/true\e[;0m"; exit 1; \
echo -e "\e[0;31;49mInvalid value for build argument INCLUDE_MONGODBTOOLS, possible values: yes/true\e[;0m"; exit 1; \
fi
RUN if ! [ -z "$INCLUDE_MONGODBTOOLS" ]; then apk add --no-cache mongodb-tools; fi
# copy files from builder-image
COPY --from=builder /opt/meshcentral/meshcentral /opt/meshcentral/meshcentral
COPY ./docker/startup.sh ./startup.sh
COPY ./docker/config.json.template /opt/meshcentral/config.json.template
COPY --from=builder /opt/meshcentral/meshcentral/docker/startup.sh ./startup.sh
COPY --from=builder /opt/meshcentral/meshcentral/docker/config.json.template /opt/meshcentral/config.json.template
# cleanup
RUN rm -rf /opt/meshcentral/meshcentral/docker
RUN rm -rf /opt/meshcentral/meshcentral/node_modules
# install dependencies from package.json
RUN cd meshcentral && npm install
# NOTE: ALL MODULES MUST HAVE A VERSION NUMBER AND THE VERSION MUST MATCH THAT USED IN meshcentral.js mainStart()
RUN if ! [ -z "$INCLUDE_MONGODBTOOLS" ]; then cd meshcentral && npm install mongodb@4.13.0 saslprep@1.0.3; fi
RUN if ! [ -z "$PREINSTALL_LIBS" ] && [ "$PREINSTALL_LIBS" == "true" ]; then cd meshcentral && npm install ssh2@1.16.0 semver@7.5.4 nodemailer@6.9.15 image-size@1.1.1 wildleek@2.0.0 otplib@10.2.3 yubikeyotp@0.2.0; fi
# install dependencies for plugins
RUN cd meshcentral && npm install nedb
EXPOSE 80 443 4433
@ -96,6 +68,6 @@ EXPOSE 80 443 4433
VOLUME /opt/meshcentral/meshcentral-data
VOLUME /opt/meshcentral/meshcentral-files
VOLUME /opt/meshcentral/meshcentral-web
VOLUME /opt/meshcentral/meshcentral-backups
VOLUME /opt/meshcentral/meshcentral-backup
CMD ["bash", "/opt/meshcentral/startup.sh"]

View file

@ -1,5 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json",
"$schema": "http://info.meshcentral.com/downloads/meshcentral-config-schema.json",
"settings": {
"plugins":{"enabled": false},
"_mongoDb": null,
@ -21,9 +21,9 @@
"": {
"_title": "MyServer",
"_title2": "Servername",
"minify": false,
"minify": true,
"NewAccounts": true,
"localSessionRecording": true,
"localSessionRecording": false,
"_userNameIsEmail": true,
"_certUrl": "my.reverse.proxy"
}

View file

@ -2,9 +2,9 @@
MSG="";
PRUNE="false";
OVERRIDE_TAGS="false";
ENABLE_LOG="false";
LOG_FILE="$(dirname -- "$( readlink -f -- "$0"; )")/build.log";
LOG_FILE=""
#LOG_FILE="$(dirname -- "$( readlink -f -- "$0"; )")/build.log";
function appendOutput()
{
@ -12,35 +12,23 @@ function appendOutput()
ARGS=$@;
LINE="${ARGS}\n";
if [ -z "${ENABLE_LOG}" ] || [ "${ENABLE_LOG}" != "true" ]; then echo -e "${LINE}" > /dev/tty; else echo -e "${LINE}" 2>&1 | tee -a ${LOG_FILE}; fi
if [ -z "${LOG_FILE}" ]; then echo -e "${LINE}" > /dev/tty; else echo -e "${LINE}" &>> "${LOG_FILE}"; fi
MSG="${MSG}${LINE}";
}
function runDockerBuild()
{
if [ "${PRUNE}" == "true" ]; then
if [ -z "${ENABLE_LOG}" ] || [ "${ENABLE_LOG}" != "true" ]; then docker system prune -a -f;
else docker system prune -a -f | tee -a ${LOG_FILE}; fi
fi
if [ "${PRUNE}" == "true" ]; then docker system prune -a -f; fi
STARTTS=$(date +%s);
ARGS=$@;
APP_VERSION=$(grep -o '"version":\s*"[^"]*"' ./package.json | cut -f4- -d\" | tr -d '"');
BASE_TAGS="";
if [ -z "${OVERRIDE_TAGS}" ] || [ "${OVERRIDE_TAGS}" != "true" ]; then
BASE_TAGS="-t meshcentral:latest -t meshcentral:${APP_VERSION}";
fi
BUILD_CMD="docker build -f docker/Dockerfile --force-rm --no-cache ${ARGS} ${BASE_TAGS} .";
BUILD_CMD="docker build -f docker/Dockerfile --force-rm --no-cache ${ARGS} -t meshcentral .";
appendOutput "Current build: ${BUILD_CMD}";
if [ -z "${ENABLE_LOG}" ] || [ "${ENABLE_LOG}" != "true" ]; then ${BUILD_CMD}; else ${BUILD_CMD} | tee -a ${LOG_FILE}; fi
if [ -z "${LOG_FILE}" ]; then ${BUILD_CMD}; else ${BUILD_CMD} &>> "${LOG_FILE}"; fi
if [ $? -ne 0 ]; then exit $?; fi
IMAGEID=$(docker images --format "{{.ID}} {{.CreatedAt}}" | sort -rk 2 | awk 'NR==1{print $1}');
appendOutput "\tImageId: ${IMAGEID}";
ENDTS=$(date +%s);
DIFSEC=$((${ENDTS}-${STARTTS}));
@ -56,7 +44,7 @@ function runDockerBuild()
else appendOutput "\tBuild time: ${TMPMIN} min ${TMPSEC} sec"; fi
else appendOutput "\tBuild time: ${DIFSEC} sec"; fi
IMG_SIZE=$(docker image inspect ${IMAGEID} | grep -o '"Size":\s*[^,]*' | cut -f2- -d ':' | tr -d ' ');
IMG_SIZE=$(docker image inspect meshcentral | grep -e "\"Size\"" | tr -d '",' | sed -E "s/\s*Size:\s*//");
expr $IMG_SIZE + 0 > /dev/null;
appendOutput "\tImage size: ${IMG_SIZE} ($((${IMG_SIZE}/1024/1024))M)\n";
@ -69,64 +57,19 @@ if [ "${parent_path}" != "$(pwd -P)" ]; then
cd "${parent_path}";
fi
if ! [ -z $1 ]; then
for arg in "$@"
do
case "${arg}" in
--prune)
PRUNE="true";
shift 1;
;;
--log)
ENABLE_LOG="true";
shift 1;
;;
--no-tags)
OVERRIDE_TAGS="true";
shift 1;
;;
--help)
__usage="\n
Usage: ./$(basename ${0}) [OPTIONS] [BUILD ARGUMENTS]\n
\n
Options:\n
\t--log \t\twrite output to build.log file\n
\t--no-tags \tdo not use default tags (meshcentral:latest and meshcentral:%VERSION%)\n
\t--prune \tWARNING: This will remove:\n
\t\t\t - all stopped docker containers\n
\t\t\t - all docker networks not used by at least one container\n
\t\t\t - all docker images without at least one container associated to them\n
\t\t\t - all docker build cache\n
\n
Build arguments: \tAll build arguments are forwarded to the docker build command, so you can use any option accepted by 'docker build'\n
\t\t\t(https://docs.docker.com/engine/reference/commandline/build/#options)\n\n
\t--build-arg INCLUDE_MONGODBTOOLS=yes \tIncludes mongodb-tools (mongodump, ...) in the image\n
\t--build-arg DISABLE_MINIFY=yes \t\tDisables minification of files\n
\t--build-arg DISABLE_TRANSLATE=yes \tDisables translation of files\n
";
echo -e $__usage;
exit 0;
;;
*)
break;
;;
esac
done
fi
if ! [ -z $1 ] && [ "${1}" == "prune" ]; then PRUNE="true"; fi
MAINARGS=$@;
#runDockerBuild --build-arg DISABLE_MINIFY=yes --build-arg DISABLE_TRANSLATE=yes;
#runDockerBuild --build-arg DISABLE_TRANSLATE=yes;
#runDockerBuild --build-arg DISABLE_MINIFY=yes;
runDockerBuild;
#runDockerBuild --build-arg DISABLE_MINIFY=yes --build-arg DISABLE_TRANSLATE=yes ${MAINARGS};
#runDockerBuild --build-arg DISABLE_TRANSLATE=yes ${MAINARGS};
#runDockerBuild --build-arg DISABLE_MINIFY=yes ${MAINARGS};
runDockerBuild ${MAINARGS};
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes --build-arg DISABLE_MINIFY=yes --build-arg DISABLE_TRANSLATE=yes ${MAINARGS};
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes --build-arg DISABLE_TRANSLATE=yes ${MAINARGS};
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes --build-arg DISABLE_MINIFY=yes ${MAINARGS};
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes ${MAINARGS};
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes --build-arg DISABLE_MINIFY=yes --build-arg DISABLE_TRANSLATE=yes;
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes --build-arg DISABLE_TRANSLATE=yes;
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes --build-arg DISABLE_MINIFY=yes;
#runDockerBuild --build-arg INCLUDE_MONGODBTOOLS=yes;
echo "";
if [ -z "${ENABLE_LOG}" ] || [ "${ENABLE_LOG}" != "true" ]; then echo -e "${MSG}"; else echo -e "${MSG}" 2>&1 | tee -a ${LOG_FILE}; fi
echo -e "${MSG}";
exit 0;

View file

@ -1,3 +1,33 @@
# How to create a docker image for meshcentral
```
> git clone https://github.com/Ylianst/MeshCentral.git
> cd MeshCentral
> docker build -f docker/Dockerfile --force-rm -t meshcentral .
# alternative, if you want to include the mongodb-tools (mongodump, ...), you can add the 'INCLUDE_MONGODBTOOLS=yes' build argument
> docker build -f docker/Dockerfile --force-rm --build-arg INCLUDE_MONGODBTOOLS=yes -t meshcentral .
# (optional) cleanup after docker build:
> cd ..
> rm -rf MeshCentral/
```
> | Argument | Description |
> | :--- | :--- |
> | -f docker/Dockerfile | Path/Name of the Dockerfile |
> | --force-rm | Always remove intermediate containers |
> | -t meshcentral | Name and optionally a tag in the 'name:tag' format |
### Optional build arguments
> | Argument | Description |
> | :--- | :--- |
> | INCLUDE_MONGODBTOOLS=yes | Includes mongodb-tools (mongodump, ...) in the image |
> | DISABLE_MINIFY=yes | Disables the minification of files |
> | DISABLE_TRANSLATE=yes | Disables the translation of files |
# Create folder-structure and files
```
@ -10,18 +40,12 @@
| - docker-compose.yml
```
# Templates
## .env
You can place the `config.json` file directly under `./meshcentral/data/`, or use the following `.env` file instead.
# Templates:
## .env:
```ini
NODE_ENV=production
USE_MONGODB=false
# set already exist mongo connection string url here
MONGO_URL=
# or set following init params for new mongodb, use it with docker-compose file with mongodb version
# initial mongodb-variables
MONGO_INITDB_ROOT_USERNAME=mongodbadmin
MONGO_INITDB_ROOT_PASSWORD=mongodbpasswd
@ -30,14 +54,15 @@ MONGO_INITDB_ROOT_PASSWORD=mongodbpasswd
# your hostname
HOSTNAME=my.domain.com
# set to your reverse proxy IP if you want to put meshcentral behind a reverse proxy
USE_MONGODB=false
# set to your reverse proxy IP if you want to put meshcentral behind a reverse proxy
REVERSE_PROXY=false
REVERSE_PROXY_TLS_PORT=
# set to true if you wish to enable iframe support
IFRAME=false
# set to false if you want disable self-service creation of new accounts besides the first (admin)
ALLOW_NEW_ACCOUNTS=true
# set to true to enable WebRTC - per documentation it is not officially released with meshcentral and currently experimental. Use with caution
# set to true to enable WebRTC - per documentation it is not officially released with meshcentral, but is solid enough to work with. Use with caution
WEBRTC=false
# set to true to allow plugins
ALLOWPLUGINS=false
@ -45,12 +70,9 @@ ALLOWPLUGINS=false
LOCALSESSIONRECORDING=false
# set to enable or disable minification of json, reduces traffic
MINIFY=true
# set this value to add extra arguments to meshcentral on startup (e.g --debug ldap)
ARGS=
```
## docker-compose.yml
## docker-compose.yml:
```yaml
version: '3'
@ -58,8 +80,7 @@ services:
meshcentral:
restart: always
container_name: meshcentral
# use the official meshcentral container
image: ghcr.io/ylianst/meshcentral:latest
image: meshcentral
ports:
# MeshCentral will moan and try everything not to use port 80, but you can also use it if you so desire, just change the config.json according to your needs
- 8086:443
@ -71,13 +92,12 @@ services:
# where file uploads for users live
- ./meshcentral/user_files:/opt/meshcentral/meshcentral-files
# location for the meshcentral-backups - this should be mounted to an external storage
- ./meshcentral/backup:/opt/meshcentral/meshcentral-backups
- ./meshcentral/backup:/opt/meshcentral/meshcentral-backup
# location for site customization files
- ./meshcentral/web:/opt/meshcentral/meshcentral-web
```
## docker-compose.yml mongodb
## docker-compose.yml mongodb:
```yaml
version: '3'
@ -101,8 +121,7 @@ services:
meshcentral:
restart: always
container_name: meshcentral
# use the official meshcentral container
image: ghcr.io/ylianst/meshcentral:latest
image: meshcentral
depends_on:
- mongodb
ports:
@ -116,7 +135,7 @@ services:
# where file uploads for users live
- ./meshcentral/user_files:/opt/meshcentral/meshcentral-files
# location for the meshcentral-backups - this should be mounted to an external storage
- ./meshcentral/backup:/opt/meshcentral/meshcentral-backups
- ./meshcentral/backup:/opt/meshcentral/meshcentral-backup
# location for site customization files
- ./meshcentral/web:/opt/meshcentral/meshcentral-web
networks:

View file

@ -1,34 +1,36 @@
#!/bin/bash
if [ -f "meshcentral-data/${CONFIG_FILE}" ]; then
node meshcentral/meshcentral --configfile "${CONFIG_FILE}" ${ARGS}
else
cp config.json.template meshcentral-data/"${CONFIG_FILE}"
if [ -n "$USE_MONGODB" ] && [ "$USE_MONGODB" == "true" ]; then
if [ -z "$MONGO_URL" ]; then
prefix=""
if [ -n "$MONGO_INITDB_ROOT_USERNAME" ] && [ -n "$MONGO_INITDB_ROOT_PASSWORD" ]; then
prefix="$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@"
fi
MONGO_URL="${prefix}mongodb:27017"
export NODE_ENV=production
export HOSTNAME
export REVERSE_PROXY
export REVERSE_PROXY_TLS_PORT
export IFRAME
export ALLOW_NEW_ACCOUNTS
export WEBRTC
export MONGO_INITDB_ROOT_USERNAME
export MONGO_INITDB_ROOT_PASSWORD
export USE_MONGODB
if [ -f "meshcentral-data/config.json" ]
then
node meshcentral/meshcentral
else
cp config.json.template meshcentral-data/config.json
if ! [ -z "$USE_MONGODB" ] && [ "$USE_MONGODB" == "true" ]; then
sed -i "s/\"_mongoDb\": null/\"mongoDb\": \"mongodb:\/\/$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@mongodb:27017\"/" meshcentral-data/config.json
fi
sed -i "s/\"_mongoDb\": null/\"mongoDb\": \"mongodb:\/\/$MONGO_URL\"/" meshcentral-data/"${CONFIG_FILE}"
fi
sed -i "s/\"cert\": \"myserver.mydomain.com\"/\"cert\": \"$HOSTNAME\"/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"NewAccounts\": true/\"NewAccounts\": $ALLOW_NEW_ACCOUNTS/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"enabled\": false/\"enabled\": $ALLOWPLUGINS/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"localSessionRecording\": false/\"localSessionRecording\": $LOCALSESSIONRECORDING/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"minify\": false/\"minify\": $MINIFY/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"WebRTC\": false/\"WebRTC\": $WEBRTC/" meshcentral-data/"${CONFIG_FILE}"
sed -i "s/\"AllowFraming\": false/\"AllowFraming\": $IFRAME/" meshcentral-data/"${CONFIG_FILE}"
if [ -z "$SESSION_KEY" ]; then
SESSION_KEY="$(cat /dev/urandom | tr -dc 'A-Z0-9' | fold -w 48 | head -n 1)"
fi
sed -i "s/\"_sessionKey\": \"MyReallySecretPassword1\"/\"sessionKey\": \"$SESSION_KEY\"/" meshcentral-data/"${CONFIG_FILE}"
if [ "$REVERSE_PROXY" != "false" ]; then
sed -i "s/\"_certUrl\": \"my\.reverse\.proxy\"/\"certUrl\": \"https:\/\/$REVERSE_PROXY:$REVERSE_PROXY_TLS_PORT\"/" meshcentral-data/"${CONFIG_FILE}"
node meshcentral/meshcentral --configfile "${CONFIG_FILE}" ${ARGS}
exit
fi
node meshcentral/meshcentral --configfile "${CONFIG_FILE}" --cert "$HOSTNAME" ${ARGS}
fi
sed -i "s/\"cert\": \"myserver.mydomain.com\"/\"cert\": \"$HOSTNAME\"/" meshcentral-data/config.json
sed -i "s/\"NewAccounts\": true/\"NewAccounts\": \"$ALLOW_NEW_ACCOUNTS\"/" meshcentral-data/config.json
sed -i "s/\"enabled\": false/\"enabled\": \"$ALLOWPLUGINS\"/" meshcentral-data/config.json
sed -i "s/\"localSessionRecording\": false/\"localSessionRecording\": \"$LOCALSESSIONRECORDING\"/" meshcentral-data/config.json
sed -i "s/\"minify\": true/\"minify\": \"$MINIFY\"/" meshcentral-data/config.json
sed -i "s/\"WebRTC\": false/\"WebRTC\": \"$WEBRTC\"/" meshcentral-data/config.json
sed -i "s/\"AllowFraming\": false/\"AllowFraming\": \"$IFRAME\"/" meshcentral-data/config.json
if [ "$REVERSE_PROXY" != "false" ]; then
sed -i "s/\"_certUrl\": \"my\.reverse\.proxy\"/\"certUrl\": \"https:\/\/$REVERSE_PROXY:$REVERSE_PROXY_TLS_PORT\"/" meshcentral-data/config.json
node meshcentral/meshcentral
exit
fi
node meshcentral/meshcentral --cert "$HOSTNAME"
fi

View file

@ -1,55 +0,0 @@
# Uses proxy protocol in HAProxy in combination with SNI to preserve the original host address
# Update the config.json to work with HAProxy
# Specify the IP addrehostname that the traffic will come from HAProxy (this might not be the address that is bound to the listener)
# "tlsOffload": "10.1.1.10",
#
# Specify the HAPRoxy URL with the hostname to get the certificate
# "certUrl": "https://mc.publicdomain.com:443/"
frontend sni-front
bind 10.1.1.10:443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
default_backend sni-back
backend sni-back
mode tcp
acl gitlab-sni req_ssl_sni -i gitlab.publicdomain.com
acl mc-sni req_ssl_sni -i mc.publicdomain.com
use-server gitlabSNI if gitlab-sni
use-server mc-SNI if mc-sni
server mc-SNI 10.1.1.10:1443 send-proxy-v2-ssl-cn
frontend cira-tcp-front
bind 10.1.1.10:4433
mode tcp
option tcplog
tcp-request inspect-delay 5s
default_backend mc-cira-back
backend cira-tcp-back
mode tcp
server mc-cira 10.1.1.30:4433
frontend mc-front-HTTPS
mode http
option forwardfor
bind 10.1.1.10:1443 ssl crt /etc/haproxy/vm.publicdomain.net.pem accept-proxy
http-request set-header X-Forwarded-Proto https
option tcpka
default_backend mc-back-HTTP
backend mc-back-HTTPS
mode http
option forwardfor
http-request add-header X-Forwarded-Host %[req.hdr(Host)]
option http-server-close
server mc-01 10.1.1.30:443 check port 443 verify none
# In the event that it is required to have TLS between HAProxy and Meshcentral,
# Remove the tls_Offload line and replace with trustedProxy
# Specify the IP addrehostname that the traffic will come from HAProxy (this might not be the address that is bound to the listener)
# "trustedProxy": "10.1.1.10",
# and change the last line of backend mc-back-HTTPS to use HTTPS by adding the ssl keyword
# server mc-01 10.1.1.30:443 check ssl port 443 verify none

View file

@ -2,7 +2,7 @@
![](images/2022-05-15-12-57-36.png)
Design and Architecture Guide [as .pdf](https://meshcentral.com/docs/MeshCentral2DesignArchitecture.pdf) [as .odt](https://github.com/Ylianst/MeshCentral/blob/master/docs/MeshCentral Design & Architecture v0.0.4.odt?raw=true)
Design and Architecture Guide [as .pdf](https://meshcentral.com/info/docs/MeshCentral2DesignArchitecture.pdf) [as .odt](https://github.com/Ylianst/MeshCentral/blob/master/docs/MeshCentral Design & Architecture v0.0.4.odt?raw=true)
## Video Walkthru
<div class="video-wrapper">
@ -108,46 +108,29 @@ Someone would think the server is rather simple when taking a look at the MeshCe
### Code files
```
amtevents.js | Used to decode Intel AMT WSMAN events.
amtmanager.js | Used to handle Intel AMT/CIRA things.
amtprovisioningserver.js | Used to Provision Intel AMT on a Local Network.
amtscanner.js | Used to scan a local network for Intel AMT machines.
amtscript.js | Used to run Intel AMT scripts from MeshCommander.
certoperations.js | Used to generate and perform certificate operations.
common.js | Various commonly used methods.
crowdsec.js | Used to handle all crowdsec security features
db.js | Used to access the MongoDB or NeDB database.
exeHandler.js | Used to modify windows executables.
firebase.js | Used to handle Google Firebase things.
interceptor.js | Used to insert credentials in an HTTP stream.
letsencrypt.js | Used to obtain and use a Lets Encrypt certificate.
mcrec.js | Standalone Session Recording Indexer.
meshaccelerator.js | Used to offload RSA sign to other CPU cores.
meshagent.js | Used to communicate to agents.
meshbot.js | Sample bot to connect to meshcentral and preform various tasks
meshcentral.js | The is the main module, gets the server started.
meshctrl.js | MeshCtrl performs command line actions on a MeshCentral server.
meshcore.js | Main Agent Code that runs on your remote devices.
meshdesktopmultiplex.js | Used to handle remote desktop multiplexing.
meshdevicefile.js | Used to handle file download requests.
meshipkvm.js | Used to handle IP KVM integration
meshmail.js | Used to send SMTP mails.
meshmessaging.js | Used to handle all users messaging methods like 2FA.
meshrelay.js | Used to relay agent and browser web socket connections.
meshscanner.js | MeshCentral server discovery when in LAN mode.
meshsms.js | Used to handle all users sms methods.
meshuser.js | Used to communicate with browsers.
mpsserver.js | Used to communicate to Intel® AMT CIRA.
mqttbroker.js | Used to create/handle an MQTT broker (beta)
multiserver.js | Used for server-to-server communication.
pass.js | Performs password hash + salt.
redirserver.js | Used to handle HTTP traffic.
swarmserver.js | Used to upgrade legacy MeshCentralv1 agents.
webauthn.js | Handles all WebAuthN things.
webrelayserver.js | Used for all HTTP/HTTPS web relaying from agents.
webserver.js | Handles HTTPS traffic.
winservice.js | Server background install on Windows.
amtevents.js | Used to scan a local network for Intel AMT machines.
amtscanner.js | Used to run Intel AMT scripts from MeshCommander.
amtscript.js | Used to generate and perform certificate operations.
certoperations.js | Various commonly used methods.
common.js | Used to access the MongoDB or NeDB database.
db.js | Used to modify windows executables.
exeHandler.js | Used to insert credentials in an HTTP stream.
interceptor.js | Used to obtain and use a Lets Encrypt certificate.
letsencrypt.js | Used to offload RSA sign to other CPU cores.
meshaccelerator.js | Used to communicate to agents.
meshagent.js | The is the main module, gets the server started.
meshcentral.js | Used to send SMTP mails.
meshmail.js | Used to relay agent and browser web socket connections.
meshrelay.js | MeshCentral server discovery when in LAN mode.
meshscanner.js | Used to communicate with browsers.
meshuser.js | Used to communicate to Intel® AMT CIRA.
mpsserver.js | Used for server-to-server communication.
multiserver.js | Performs password hash + salt.
pass.js | Used to handle HTTP traffic.
redirserver.js | Used to upgrade legacy MeshCentralv1 agents.
swarmserver.js | Handles HTTPS traffic.
webserver.js | Server background install on Windows.
winservice.js | Server background install on Windows.
```
At a high level, the MeshCentral.js file will get the server started. By default, it will start the webserver.js on port 443, redirectserver.js on port 80 and mpssrver.js on port 4433. The webserver.js file will create a meshuser.js or meshagent.js instance each time a user or agent connects. The other files support various usages, but this is the basic working on the server.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,56 +0,0 @@
# Contribute to MeshCentral
## Contributing to MeshCentral via GitHub Pull Request
If you're looking to contribute beyond translations, such as updating documentation or enhancing the software by adding features or fixing bugs, the process involves several key steps:
1. **Fork the Repository:** Start by forking the [MeshCentral](https://github.com/Ylianst/MeshCentral) repository on GitHub. This creates a copy of the repository under your own GitHub account, allowing you to make changes without affecting the original project.
2. **Make Your Changes**
- In your forked repository, create a new branch to keep your changes organized. This helps in managing different contributions separately.
- Make the necessary changes in your repository. This could involve updating documentation files or modifying code to add new features or fix bugs.
3. **Review Your Changes:** Before submitting your work, carefully review the changes youve made. Check the "Files Changed" section on GitHub to ensure that all modifications are intended and correctly implemented.
4. **Submit a Pull Request**
- Once your changes are ready and reviewed, submit a pull request (PR) from your branch to the `master` branch of the main MeshCentral repository.
- When creating the pull request, provide a clear and detailed description of what changes have been made and why. This helps maintainers understand the purpose of your contributions.
5. **Wait for Review:** After submitting your pull request, wait for a project maintainer to review your contribution. Review time can vary depending on the complexity of the changes and the availability of the maintainers.
6. **Respond to Feedback:** The maintainer may request further modifications or provide feedback on your pull request. Be prepared to make additional changes based on their suggestions to ensure that your contribution meets the projects standards and requirements.
7. **Final Steps:** Once your pull request is approved and merged by a maintainer, your contributions will be incorporated into the MeshCentral project. Congratulations, and thank you for helping improve MeshCentral!
---
## Contribute to MeshCentral's Multilingual Support
To make MeshCentral multilingual, your contributions are crucial. Follow these steps to translate the interface into various languages.
1. **Remove Local Translations:** Delete `translate.json` from your `meshcentral-data` folder. This file contains your local copy of translations, which may become outdated as new features and texts are added.
2. **Access MeshCentral:** Ensure you are logged into MeshCentral.
3. **Open Translation Tool:** Visit `https://YOURMESHCENTRALSERVER.COM/translator.htm` to access the translation interface.
4. **Choose a Language:** Select the language you wish to translate from the list provided.
5. **Translate Text:** Use the search function or scroll through the list to find text segments you want to translate. Utilize the "show no translations only" checkbox to filter untranslated texts.
6. **Enter Translations:** For each text segment, enter your translation in the bottom box (not the top one) and click `SET (F1)`.
7. **Repeat Translation:** Continue translating by repeating steps 5 and 6 for other texts as desired.
8. **Save and Apply Translations**
- Click `SAVE TO SERVER (F3)` to save your translations to `meshcentral-data/translate.json` locally in your MeshCentral server.
- Optionally, click `SAVE TO FILE (F4)` to download the `translate.json` file for offline review or sharing.
9. **Deploy Translations:** Click `TRANSLATE SERVER` and allow some time for the process to complete (approximately 5-15 minutes depending on server specifications). This command line output will indicate when the translation is complete.
![](images/translation-msg-output.png)
10. **Finalize Changes:** Its crucial to restart MeshCentral to ensure that the translated files are picked up correctly.
11. **Share your translations:** Once a language translation is complete, take the latest `translation.json` and share it by emailing it to the maintainer (Ylianst, `ylianst@gmail.com`) or by submitting it to the MeshCentral GitHub repository via a pull request.
---
#### Additional Information:
- If you make any changes to `default.handlebars`, run the translate server to propagate these modifications to the language-specific handlebar files located in `node_modules/meshcentral/views/translations`.
By following these steps, you help MeshCentral support any language you choose, making it more accessible worldwide. By sharing your translations with us, you also help make these languages available to other users, improving the community and extending the software's reach.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -4,6 +4,8 @@
MeshCentral is a full computer management web site. With MeshCentral, you can run your own web server to remotely manage and control computers on a local network or anywhere on the internet. Once you get the server started, create device group and download and install an agent on each computer you want to manage. A minute later, the new computer will show up on the web site and you can take control of it. MeshCentral includes full web-based remote desktop, terminal and file management capability.
To try out this software on the public server, please visit [MeshCentral.com/login](https://meshcentral.com/login). Be mindful that the public MeshCentral server comes with no guaranties, most should setup their own server.
For more information, [visit MeshCentral.com](https://www.meshcentral.com/).
## Social Media
@ -12,17 +14,17 @@ For more information, [visit MeshCentral.com](https://www.meshcentral.com/).
[Reddit](https://www.reddit.com/r/MeshCentral/)
[BlueSky](https://bsky.app/profile/meshcentral.bsky.social)
[Twitter](https://twitter.com/MeshCentral)
[BlogSpot](https://meshcentral2.blogspot.com/)
## Documentation
The [User's Guide](meshcentral) contains information every administrator should know including usage, the server configuration file, databases, TLS offloading, Lets Encrypt, IP Filtering, Email setup, embedding, server port aliasing, reverse proxy setup, multi factor authentication, branding & terms of use, HashiCorp Vault support, and SSO.
The [User's Guide](https://info.meshcentral.com/downloads/MeshCentral2/MeshCentral2UserGuide.pdf) contains information every adminstrator should know including usage, the server configuration file, databases, TLS offloading, Lets Encrypt, IP Filtering, Email setup, embedding, server port aliasing, reverse proxy setup, multi factor authentication, branding & terms of use, HashiCorp Vault support, and SSO.
The [Installation Guide](install/install2.md) has detailed instructions for installing the MeshCentral Server on Windows 8.1, Windows 10, Windows 2012 R2, Amazon Linux 2, Raspberry Pi, Microsoft Azure, Google Cloud, Ubuntu 18, Ubuntu 16 and OpenBSD.
The [Installation Guide](https://info.meshcentral.com/downloads/MeshCentral2/MeshCentral2InstallGuide.pdf) has detailed instructions for installing the MeshCentral Server on Windows 8.1, Windows 10, Windows 2012 R2, Amazon Linux 2, Raspberry Pi, Microsoft Azure, Google Cloud, Ubuntu 18, Ubuntu 16 and OpenBSD.
The [Design and Architecture Guide](design) is a short document that includes information on the design overview, dependencies, source code descriptions of each file, certificates, TLS security, the agent to server handshake, browser to agent relay and WebRTC and the messenger service.
The [Design and Architecture Guide](https://info.meshcentral.com/downloads/MeshCentral2/MeshCentral2DesignArchitecture.pdf) is a short document that includes information on the design overview, dependencies, source code descriptions of each file, certificates, TLS security, the agent to server handshake, browser to agent relay and WebRTC and the messenger service.
## Video Tutorials

View file

@ -9,11 +9,11 @@ npm install meshcentral
node node_modules/meshcentral
```
That's it. MeshCentral will set itself up and start managing computers on your local network. By default it will be setup in LAN mode and agents you install will multicast on the local network to find the server. To setup the server so that agents use a well known DNS name and to start customizing your server, go in the "meshcentral-data" folder and edit the config.json file. The configuration file must be valid JSON, you can use this [link](https://duckduckgo.com/?va=j&t=hc&q=json+lint&ia=answer) to validate the file format.
That's it. MeshCentral will set itself up and start managing computers on your local network. By default it will be setup in LAN mode and agents you install will multicast on the local network to find the server. To setup the server so that agents use a well known DNS name and to start customizing your server, go in the "meshcentral-data" folder and edit the config.json file. The configuration file must be valid JSON, you can use this link to validate the file format.
For Windows users, you can download the MeshCentral Installer that will automate installation of NodeJS and provide basic configuration of the server. This option is not recommended for advanced users.
[Win32 MeshCentral Installer](https://meshcentral.com/tools/MeshCentralInstaller.exe)
[Win32 MeshCentral Installer](https://meshcentral.com/info/tools/MeshCentralInstaller.exe)
By default, MeshCentral will use NeDB as this is the built-in database. For more advanced users, it's recommended to switch to using MongoDB. MeshCentral can be installed on a very small server. A [Raspberry Pi](https://www.raspberrypi.org/) or [AWS t3.nano running Amazon Linux 2 instance](https://aws.amazon.com/ec2/pricing/on-demand/) for 5$ a month will do just fine for managing up to a few hundred devices.
@ -23,10 +23,10 @@ You can run the MeshCentral Server with --help to get options for background ins
Once you get MeshCentral installed, the first user account that is created will be the server administrator. So, don't delay and navigate to the login page and create a new account. You can then start using your server right away. A lot of the fun with MeshCentral is the 100's of configuration options that are available in the config.json file. You can put your own branding on the web pages, setup a SMTP email server, SMS services and much more.
You can look [here for simple config.json](https://raw.githubusercontent.com/Ylianst/MeshCentral/master/sample-config.json), [here for a more advanced configuration](https://raw.githubusercontent.com/Ylianst/MeshCentral/master/sample-config-advanced.json) and [here for all possible configuration options](https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json). You can also take a look at the [tutorial videos](https://www.youtube.com/@MeshCentral/videos) for additional help.
You can look [here for simple config.json](https://raw.githubusercontent.com/Ylianst/MeshCentral/master/sample-config.json), [here for a more advanced configuration](https://raw.githubusercontent.com/Ylianst/MeshCentral/master/sample-config-advanced.json) and [here for all possible configuration options](https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json). You can also take a look at the [MeshCentral User's Guide](https://meshcentral.com/info/docs/MeshCentral2InstallGuide.pdf) and [tutorial videos](https://meshcentral.com/info/tutorials.html) for additional help.
## Video Walkthru
<div class="video-wrapper">
<iframe width="320" height="180" src="https://www.youtube.com/embed/LSiWuu71k_U" frameborder="0" allowfullscreen></iframe>
</div>
</div>

View file

@ -4,45 +4,6 @@
This guide is specifically intended to help users install MeshCentral from start to finish. Once installed, you can take a look at the MeshCentral users guide for information on how to configure MeshCentral for your specific use. In this document, we will look at installing MeshCentral on AWS Linux, Raspberry Pi and Ubuntu.
## Docker
<https://github.com/Ylianst/MeshCentral/pkgs/container/meshcentral>
```
docker pull ghcr.io/ylianst/meshcentral:master
```
!!!warning
Do not use the built in mesh update function. Update docker the docker way.
### Docker Compose
```
version: '3'
services:
meshcentral:
restart: unless-stopped # always restart the container unless you stop it
image: ghcr.io/ylianst/meshcentral:1.1.27 # 1.1.27 is a version number OR use master for the master branch of bug fixes
ports:
- 80:80 # HTTP
- 443:443 # HTTPS
- 4433:4433 # AMT (Optional)
volumes:
- data:/opt/meshcentral/meshcentral-data # config.json and other important files live here
- user_files:/opt/meshcentral/meshcentral-files # where file uploads for users live
- backup:/opt/meshcentral/meshcentral-backups # location for the meshcentral backups - this should be mounted to an external storage
- web:/opt/meshcentral/meshcentral-web # location for site customization files
volumes:
data:
driver: local
user_files:
driver: local
backup:
driver: local
web:
driver: local
```
## Quick Start
For some who want to skip this document entirely, there are quick install scripts that will get a MeshCentral2 instance up and running on Linux in a few minutes. These scripts will pretty much do what this document explains very rapidly. Right now, there are two such scripts available:
@ -52,7 +13,7 @@ For some who want to skip this document entirely, there are quick install script
For Amazon EC2 users, that want to manage 100 devices or less. Launch a t3.nano or t3.micro EC2 instance with Amazon Linux 2 with TCP ports 22 (SSH), 80 (HTTP), 443 (HTTPS) and 4433 (CIRA) open. Then login as `ec2-user` and enter the following commands:
```
wget https://meshcentral.com/scripts/mc-aws-linux2.sh
wget http://info.meshcentral.com/scripts/mc-aws-linux2.sh
chmod 755 mc-aws-linux2.sh
./mc-aws-linux2.sh
```
@ -60,7 +21,7 @@ chmod 755 mc-aws-linux2.sh
This will download the fast install script and once run, will install nodejs, meshcentral, setup systemd and start the server. For a larger instance like a t3.small, t3.medium or larger you can run the following that does the same but also installs MongoDB.
```
wget https://meshcentral.com/scripts/mc-aws-linux2-mongo.sh
wget http://info.meshcentral.com/scripts/mc-aws-linux2-mongo.sh
chmod 755 mc-aws-linux2-mongo.sh
./mc-aws-linux2-mongo.sh
```
@ -72,29 +33,13 @@ After these scripts are run, try accessing the server using a browser. MeshCentr
For 100 devices or less, launch an instance of Ubuntu 18.04 using a small B1s instance. Set the username to `default` in all lower case and open ports 22, 80, 443 and 3389 using the basic network profile. Then start the instance and run the following lines.
```
wget https://meshcentral.com/scripts/mc-azure-ubuntu1804.sh
wget http://info.meshcentral.com/scripts/mc-azure-ubuntu1804.sh
chmod 755 mc-azure-ubuntu1804.sh
./mc-azure-ubuntu1804.sh
```
In this situation, port 3389 will be used to receive Intel AMT CIRA connections instead of port 4433. After these scripts are run, try accessing the server using a browser. MeshCentral will take a minute or two to create certificates after that, the server will be up. The first account to be created will be the site administrator so dont delay and create an account right away. Once running, move on to the MeshCentrals users guide to configure your new server.
### Elestio
You can deploy MeshCentral on Elestio using one-click deployment. Elestio handles version updates, maintenance, securtiy, backups, etc. Additionally, Elestio supports MeshCentral by providing revenue share so go ahead and click below to deploy and start using.
[![Deploy on Elestio](https://elest.io/images/logos/deploy-to-elestio-btn.png)](https://elest.io/open-source/meshcentral)
## Server Security - Adding Crowdsec
MeshCentral has built-in support for a CrowdSec bouncer. This allows MeshCentral to get threat signals from the community and block or CAPTCHA requests coming from known bad IP addresses.
## Video Walkthru
<div class="video-wrapper">
<iframe width="320" height="180" src="https://www.youtube.com/embed/TVKF9gBJFCE" frameborder="0" allowfullscreen></iframe>
</div>
## Windows Installation
MeshCentral is constructed entirely with NodeJS, an asynchronous event driven JavaScript runtime (https://nodejs.org/). A basic understanding on NodeJS may be preferable but not compulsory. MeshCentral server which heavily relies on NodeJS runtime will be able run on almost any computing platform with contemporary operating systems including Windows*, Linux* and macOS*.
@ -929,7 +874,7 @@ The last line will run MeshCentral manually and allow it to install any missing
```
sudo chown -R meshcentral:meshcentral /opt/meshcentral
sudo chmod -R 755 /opt/meshcentral/meshcentral-*
sudo chmod 755 R /opt/meshcentral/meshcentral-*
```
To make this work, you will need to make MeshCentral work with MongoDB because the /meshcentral-data folder will be read-only. In addition, MeshCentral will not be able to update itself since the account does not have write access to the /node_modules files, so the update will have to be manual. First used systemctl to stop the MeshCentral server process, than use this:
@ -946,7 +891,7 @@ This will perform the update to the latest server on NPM and re-set the permissi
MeshCentral allows users to upload and download files stores in the servers `meshcentral-files` folder. In an increased security setup, we still want the server to be able to read and write files to this folder and we can allow this with:
```
sudo chmod -R 755 /opt/meshcentral/meshcentral-files
sudo chmod 755 R /opt/meshcentral/meshcentral-files
```
If you plan on using the increased security installation along with MeshCentral built-in Lets Encrypt support you will need to type the following commands to make the `letsencrypt` folder in `meshcentral-data` writable.
@ -954,19 +899,11 @@ If you plan on using the increased security installation along with MeshCentral
```
sudo mkdir /opt/meshcentral/meshcentral-data
sudo mkdir /opt/meshcentral/meshcentral-data/letsencrypt
sudo chmod -R 755 /opt/meshcentral/meshcentral-data/letsencrypt
sudo chmod 755 R /opt/meshcentral/meshcentral-data/letsencrypt
```
This will allow the server to get and periodically update its Lets Encrypt certificate. If this is not done, the server will generate an `ACCES: permission denied` exception.
### Restore backup in Ubuntu
- Stop Meshcentral service `sudo systemctl stop meshcentral.service`
- In your old server, get your backup : meshcentral-data folder, and mongodump-xxxx.archive
- In the new server, replace the actual meshcentral-data with your backup (it will handle your LestEncrypt cert also)
- Restore mongodb : mongorestore --archive=mongodump-xxxx.archive
- Restart meshcentral.service `sudo systemctl start meshcentral.service`
## Microsoft Azure
In this section, we will look installing MeshCentral on Microsoft Azure. Microsoft Azure offers many operating system options and we will be selecting `Ubuntu Server` as our choice. From the Azure portal, we select `Virtual machines` on the left and `Add`.

Some files were not shown because too many files have changed in this diff Show more