Added Intel AMT tunneling configuration support.

This commit is contained in:
Ylian Saint-Hilaire 2020-10-17 19:23:24 -07:00
parent 9aab9a98e3
commit c0eab31b02
8 changed files with 709 additions and 109 deletions

View File

@ -113,7 +113,7 @@ function run(argv) {
//console.log('addedModules = ' + JSON.stringify(addedModules));
var actionpath = 'meshaction.txt';
if (args.actionfile != null) { actionpath = args.actionfile; }
var actions = ['HELP', 'ROUTE', 'MICROLMS', 'AMTSCAN', 'AMTPOWER', 'AMTFEATURES', 'AMTNETWORK', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'AMTINFO', 'AMTINFODEBUG', 'AMTVERSIONS', 'AMTHASHES', 'AMTSAVESTATE', 'AMTSCRIPT', 'AMTUUID', 'AMTCCM', 'AMTACM', 'AMTDEACTIVATE', 'AMTACMDEACTIVATE', 'SMBIOS', 'RAWSMBIOS', 'MESHCOMMANDER', 'AMTAUDITLOG', 'AMTEVENTLOG', 'AMTPRESENCE', 'AMTWIFI', 'AMTWAKE'];
var actions = ['HELP', 'ROUTE', 'MICROLMS', 'AMTCONFIG', 'AMTSCAN', 'AMTPOWER', 'AMTFEATURES', 'AMTNETWORK', 'AMTLOADWEBAPP', 'AMTLOADSMALLWEBAPP', 'AMTLOADLARGEWEBAPP', 'AMTCLEARWEBAPP', 'AMTSTORAGESTATE', 'AMTINFO', 'AMTINFODEBUG', 'AMTVERSIONS', 'AMTHASHES', 'AMTSAVESTATE', 'AMTSCRIPT', 'AMTUUID', 'AMTCCM', 'AMTACM', 'AMTDEACTIVATE', 'AMTACMDEACTIVATE', 'SMBIOS', 'RAWSMBIOS', 'MESHCOMMANDER', 'AMTAUDITLOG', 'AMTEVENTLOG', 'AMTPRESENCE', 'AMTWIFI', 'AMTWAKE'];
// Load the action file
var actionfile = null;
@ -129,6 +129,7 @@ function run(argv) {
if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); }
if ((typeof args.remotenodeid) == 'string') { settings.remotenodeid = args.remotenodeid; }
if ((typeof args.name) == 'string') { settings.name = args.name; }
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.url) == 'string') { settings.url = args.url; }
@ -174,6 +175,7 @@ function run(argv) {
console.log(' meshcmd [action] [arguments...]\r\n');
console.log('Valid MeshCentral actions:');
console.log(' Route - Map a local TCP port to a remote computer.');
console.log(' AmtConfig - Setup Intel AMT on this computer.');
console.log('\r\nValid local actions:');
console.log(' SMBios - Display System Management BIOS tables for this computer.');
console.log(' RawSMBios - Display RAW System Management BIOS tables for this computer.');
@ -245,6 +247,12 @@ function run(argv) {
console.log(' --tag [string] Optional string sent to the server during activation.');
console.log(' --serverhttpshash [hash] Optional TLS server certificate hash.');
console.log(' --profile [string] Optional profile used for server activation.');
} else if (action == 'amtconfig') {
console.log('AmtConfig will attempt to activate and configure Intel AMT on this computer. The command must be run on a computer with Intel AMT, must run as administrator and the Intel management driver must be installed. Example usage:\r\n\r\n meshcmd amtconfig --url [url]');
console.log('\r\nPossible arguments:\r\n');
console.log(' --url [wss://server] The address of the MeshCentral server.');
console.log(' --id [groupid] The device group identifier.');
console.log(' --serverhttpshash [hash] Optional TLS server certificate hash.');
} else if (action == 'amtacm') {
console.log('AmtACM will attempt to activate Intel AMT on this computer into admin control mode (ACM). The command must be run on a computer with Intel AMT, must run as administrator and the Intel management driver must be installed. Intel AMT must be in "pre-provisioning" state for this command to work. Example usage:\r\n\r\n meshcmd amtacm --url [url]');
console.log('\r\nPossible arguments:\r\n');
@ -643,6 +651,12 @@ function run(argv) {
settings.localport = 16992;
debug(1, "Settings: " + JSON.stringify(settings));
getAmtUuid();
} else if (settings.action == 'amtconfig') {
// 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; }
debug(1, "Settings: " + JSON.stringify(settings));
configureAmt();
} else if (settings.action == 'amtccm') {
// Start activation to CCM
if (((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) && ((settings.url == null) || (typeof settings.url != 'string') || (settings.url == ''))) { console.log('No or invalid parameters specified, use --password [password] or --url [url].'); exit(1); return; }
@ -1127,6 +1141,88 @@ function startMeshCommander() {
}
//
// Configure Intel AMT
//
function configureAmt() {
console.log('Starting Intel AMT configuration...');
settings.noconsole = true;
// Display Intel AMT version and activation state
mestate = {};
var amtMeiModule, amtMei;
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { console.log(ex); exit(1); return; }
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
amtMei.getProvisioningState(function (result) { if (result) { mestate.ProvisioningState = result; } });
amtMei.getVersion(function (val) { mestate.vers = {}; if (val != null) { for (var version in val.Versions) { mestate.vers[val.Versions[version].Description] = val.Versions[version].Version; } } });
amtMei.getLanInterfaceSettings(0, function (result) { if (result) { mestate.net0 = result; } });
amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { mestate.uuid = result.uuid; } });
amtMei.getControlMode(function (result) { if (result != null) { mestate.controlMode = result.controlMode; } }); // controlMode: 0 = NoActivated, 1 = CCM, 2 = ACM
amtMei.getDnsSuffix(function (result) {
if ((mestate.vers == null) || (mestate.vers['AMT'] == null)) { console.log("Unable to get Intel AMT version."); exit(100); return; }
if (mestate.ProvisioningState == null) { console.log("Unable to read Intel AMT activation state."); exit(100); return; }
//if ((settings.action != 'amtdiscover') && (mestate.controlMode == 2)) { console.log("Intel AMT already activation in admin control mode."); exit(100); return; }
if (mestate.uuid == null) { console.log("Unable to get Intel AMT UUID."); exit(100); return; }
var fqdn = null;
//if ((mestate.net0 == null) && (meinfo.net0.enabled != 0)) { console.log("No Intel AMT wired interface, can't perform ACM activation."); exit(100); return; }
if (result) { fqdn = result; } // If Intel AMT has a trusted DNS suffix set, use that one.
else {
// Look for the DNS suffix for the Intel AMT Ethernet interface
var interfaces = require('os').networkInterfaces();
for (var i in interfaces) {
for (var j in interfaces[i]) {
if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { fqdn = interfaces[i][j].fqdn; }
}
}
}
if (fqdn != null) { settings.fqdn = fqdn; settings.uuid = mestate.uuid; }
getTrustedHashes(amtMei, function () { startLms(configureAmt2, amtMei); });
});
}
function configureAmt2() {
// Connect to MPS and start APF relay
var apfarg = {
mpsurl: settings.url,
mpsuser: settings.id.substring(0, 16),
mpspass: settings.id.substring(0, 16),
mpskeepalive: 60000,
clientname: require('os').hostname(),
clientaddress: '127.0.0.1',
clientuuid: mestate.uuid,
conntype: 2 // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay.
};
if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
console.log("Unable to get Intel AMT UUID: " + apfarg.clientuuid);
exit(1); return;
} else {
settings.apftunnel = require('apfclient')({ debug: (settings.debuglevel > 0) }, apfarg);
settings.apftunnel.onJsonControl = configureJsonControl;
settings.apftunnel.onChannelClosed = function () { exit(0); }
try {
settings.apftunnel.connect();
console.log("Started APF tunnel...");
} catch (e) {
console.log(JSON.stringify(e));
exit(1); return;
}
}
}
function configureJsonControl(data) {
switch (data.action) {
case 'console':
console.log(data.msg);
break;
case 'close':
exit(0);
break;
}
}
//
// Deactivate Intel AMT CCM
//

View File

@ -2563,7 +2563,7 @@ function createMeshCore(agent) {
var response = null;
switch (cmd) {
case 'help': { // Displays available commands
var fin = '', f = '', availcommands = 'coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
var fin = '', f = '', availcommands = 'amtconfig,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
if (process.platform != 'freebsd') { availcommands += ',vm';}
if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
@ -3540,6 +3540,29 @@ function createMeshCore(agent) {
if (diag) { diag.close(); diag = null; }
break;
}
case 'amtconfig': {
if (meshCoreObj.intelamt == null) { response = "No Intel AMT support delected"; break; }
if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
var apfarg = {
mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpskeepalive: 60000,
clientname: require('os').hostname(),
clientaddress: '127.0.0.1',
clientuuid: meshCoreObj.intelamt.uuid,
conntype: 2 // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
};
if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) { response = "Unable to get Intel AMT UUID"; break; }
apftunnel = require('apfclient')({ debug: false }, apfarg);
apftunnel.onJsonControl = function (data) {
if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
}
apftunnel.onChannelClosed = function () { apftunnel = null; }
try { apftunnel.connect(); response = "Started Intel AMT configuration"; } catch (ex) { response = JSON.stringify(ex); }
break;
}
case 'apf': {
if (meshCoreObj.intelamt !== null) {
if (args['_'].length == 1) {
@ -3562,8 +3585,12 @@ function createMeshCore(agent) {
if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
} else {
var tobj = { debug: false };
apftunnel = require('apfclient')(tobj, apfarg);
apftunnel = require('apfclient')({ debug: false }, apfarg);
apftunnel.onJsonControl = function (data) {
if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
}
apftunnel.onChannelClosed = function () { apftunnel = null; }
try {
apftunnel.connect();
response = "Started APF tunnel";

View File

@ -0,0 +1,450 @@
/*
Copyright 2018-2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @description APF/CIRA Client for Duktape
* @author Joko Sastriawan & Ylian Saint-Hilaire
* @copyright Intel Corporation 2020
* @license Apache-2.0
* @version v0.0.2
*/
function CreateAPFClient(parent, args) {
if ((args.clientuuid == null) || (args.clientuuid.length != 36)) return null; // Require a UUID if this exact length
var obj = {};
obj.parent = parent;
obj.args = args;
obj.http = require('http');
obj.net = require('net');
obj.forwardClient = null;
obj.downlinks = {};
obj.pfwd_idx = 0;
obj.timer = null; // Keep alive timer
// obj.onChannelClosed
// obj.onJsonControl
// Function copied from common.js
function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
function hex2rstr(d) { var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) { r += String.fromCharCode('0x' + t); } return r; };
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }; // Convert decimal to hex
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }; // Convert a raw string to a hex string
function d2h(d) { return (d / 256 + 1 / 512).toString(16).substring(2, 4); }
function buf2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += d2h(input[i]); } return r; };
function Debug(str) { if (obj.parent.debug) { console.log(str); } }
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
function strToGuid(s) { s = s.replace(/-/g, ''); var ret = s.substring(6, 8) + s.substring(4, 6) + s.substring(2, 4) + s.substring(0, 2) + s.substring(10, 12) + s.substring(8, 10) + s.substring(14, 16) + s.substring(12, 14) + s.substring(16, 20) + s.substring(20); return ret; }
function binzerostring(len) { var res = ''; for (var l = 0; l < len; l++) { res += String.fromCharCode(0 & 0xFF); } return res; }
// CIRA state
var CIRASTATE = {
INITIAL: 0,
PROTOCOL_VERSION_SENT: 1,
AUTH_SERVICE_REQUEST_SENT: 2,
AUTH_REQUEST_SENT: 3,
PFWD_SERVICE_REQUEST_SENT: 4,
GLOBAL_REQUEST_SENT: 5,
FAILED: -1
}
obj.cirastate = CIRASTATE.INITIAL;
// REDIR state
var REDIR_TYPE = {
REDIR_UNKNOWN: 0,
REDIR_SOL: 1,
REDIR_KVM: 2,
REDIR_IDER: 3
}
// redirection start command
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
// Intel AMT forwarded port list for non-TLS mode
//var pfwd_ports = [16992, 623, 16994, 5900];
var pfwd_ports = [ 16992, 16993 ];
// protocol definitions
var APFProtocol = {
UNKNOWN: 0,
DISCONNECT: 1,
SERVICE_REQUEST: 5,
SERVICE_ACCEPT: 6,
USERAUTH_REQUEST: 50,
USERAUTH_FAILURE: 51,
USERAUTH_SUCCESS: 52,
GLOBAL_REQUEST: 80,
REQUEST_SUCCESS: 81,
REQUEST_FAILURE: 82,
CHANNEL_OPEN: 90,
CHANNEL_OPEN_CONFIRMATION: 91,
CHANNEL_OPEN_FAILURE: 92,
CHANNEL_WINDOW_ADJUST: 93,
CHANNEL_DATA: 94,
CHANNEL_CLOSE: 97,
PROTOCOLVERSION: 192,
KEEPALIVE_REQUEST: 208,
KEEPALIVE_REPLY: 209,
KEEPALIVE_OPTIONS_REQUEST: 210,
KEEPALIVE_OPTIONS_REPLY: 211,
JSON_CONTROL: 250 // This is a Mesh specific command that sends JSON to and from the MPS server.
}
var APFDisconnectCode = {
HOST_NOT_ALLOWED_TO_CONNECT: 1,
PROTOCOL_ERROR: 2,
KEY_EXCHANGE_FAILED: 3,
RESERVED: 4,
MAC_ERROR: 5,
COMPRESSION_ERROR: 6,
SERVICE_NOT_AVAILABLE: 7,
PROTOCOL_VERSION_NOT_SUPPORTED: 8,
HOST_KEY_NOT_VERIFIABLE: 9,
CONNECTION_LOST: 10,
BY_APPLICATION: 11,
TOO_MANY_CONNECTIONS: 12,
AUTH_CANCELLED_BY_USER: 13,
NO_MORE_AUTH_METHODS_AVAILABLE: 14,
INVALID_CREDENTIALS: 15,
CONNECTION_TIMED_OUT: 16,
BY_POLICY: 17,
TEMPORARILY_UNAVAILABLE: 18
}
var APFChannelOpenFailCodes = {
ADMINISTRATIVELY_PROHIBITED: 1,
CONNECT_FAILED: 2,
UNKNOWN_CHANNEL_TYPE: 3,
RESOURCE_SHORTAGE: 4,
}
var APFChannelOpenFailureReasonCode = {
AdministrativelyProhibited: 1,
ConnectFailed: 2,
UnknownChannelType: 3,
ResourceShortage: 4,
}
obj.onSecureConnect = function onSecureConnect(resp, ws, head) {
Debug("APF Secure WebSocket connected.");
//console.log(JSON.stringify(resp));
obj.forwardClient.tag = { accumulator: [] };
obj.forwardClient.ws = ws;
obj.forwardClient.ws.on('end', function () {
Debug("APF: Connection is closing.");
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
if (obj.onChannelClosed) { obj.onChannelClosed(obj); }
});
obj.forwardClient.ws.on('data', function (data) {
obj.forwardClient.tag.accumulator += hex2rstr(buf2hex(data));
try {
var len = 0;
do {
len = ProcessData(obj.forwardClient);
if (len > 0) { obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len); }
if (obj.cirastate == CIRASTATE.FAILED) {
Debug("APF: in a failed state, destroying socket.");
obj.forwardClient.ws.end();
}
} while (len > 0);
} catch (ex) { Debug(ex); }
});
obj.forwardClient.ws.on('error', function (e) {
Debug("APF: Connection error, ending connecting.");
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
});
obj.state = CIRASTATE.INITIAL;
if ((typeof obj.args.conntype == 'number') && (obj.args.conntype != 0)) { SendJsonControl(obj.forwardClient.ws, { action: 'connType', value: obj.args.conntype } ); }
SendProtocolVersion(obj.forwardClient.ws, obj.args.clientuuid);
SendServiceRequest(obj.forwardClient.ws, 'auth@amt.intel.com');
}
function SendJsonControl(socket, o) {
var data = JSON.stringify(o)
socket.write(String.fromCharCode(APFProtocol.JSON_CONTROL) + IntToStr(data.length) + data);
Debug("APF: Send JSON control: " + data);
}
function SendProtocolVersion(socket, uuid) {
var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(strToGuid(uuid)) + binzerostring(64);
socket.write(data);
Debug("APF: Send protocol version 1 0 " + uuid);
obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
}
function SendServiceRequest(socket, service) {
var data = String.fromCharCode(APFProtocol.SERVICE_REQUEST) + IntToStr(service.length) + service;
socket.write(data);
Debug("APF: Send service request " + service);
if (service == 'auth@amt.intel.com') {
obj.cirastate = CIRASTATE.AUTH_SERVICE_REQUEST_SENT;
} else if (service == 'pfwd@amt.intel.com') {
obj.cirastate = CIRASTATE.PFWD_SERVICE_REQUEST_SENT;
}
}
function SendUserAuthRequest(socket, user, pass) {
var service = "pfwd@amt.intel.com";
var data = String.fromCharCode(APFProtocol.USERAUTH_REQUEST) + IntToStr(user.length) + user + IntToStr(service.length) + service;
//password auth
data += IntToStr(8) + 'password';
data += binzerostring(1) + IntToStr(pass.length) + pass;
socket.write(data);
Debug("APF: Send username password authentication to MPS");
obj.cirastate = CIRASTATE.AUTH_REQUEST_SENT;
}
function SendGlobalRequestPfwd(socket, amthostname, amtport) {
var tcpipfwd = 'tcpip-forward';
var data = String.fromCharCode(APFProtocol.GLOBAL_REQUEST) + IntToStr(tcpipfwd.length) + tcpipfwd + binzerostring(1, 1);
data += IntToStr(amthostname.length) + amthostname + IntToStr(amtport);
socket.write(data);
Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport);
obj.cirastate = CIRASTATE.GLOBAL_REQUEST_SENT;
}
function SendKeepAliveRequest(socket) {
socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + IntToStr(255));
Debug("APF: Send keepalive request");
}
function SendKeepAliveReply(socket, cookie) {
socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + IntToStr(cookie));
Debug("APF: Send keepalive reply");
}
function ProcessData(socket) {
var cmd = socket.tag.accumulator.charCodeAt(0);
var len = socket.tag.accumulator.length;
var data = socket.tag.accumulator;
if (len == 0) { return 0; }
// Respond to MPS according to obj.cirastate
switch (cmd) {
case APFProtocol.SERVICE_ACCEPT: {
var slen = ReadInt(data, 1), service = data.substring(5, 6 + slen);
Debug("APF: Service request to " + service + " accepted.");
if (service == 'auth@amt.intel.com') {
if (obj.cirastate >= CIRASTATE.AUTH_SERVICE_REQUEST_SENT) {
SendUserAuthRequest(socket.ws, obj.args.mpsuser, obj.args.mpspass);
}
} else if (service == 'pfwd@amt.intel.com') {
if (obj.cirastate >= CIRASTATE.PFWD_SERVICE_REQUEST_SENT) {
SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
}
}
return 5 + slen;
}
case APFProtocol.REQUEST_SUCCESS: {
if (len >= 5) {
var port = ReadInt(data, 1);
Debug("APF: Request to port forward " + port + " successful.");
// iterate to pending port forward request
if (obj.pfwd_idx < pfwd_ports.length) {
SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
} else {
// no more port forward, now setup timer to send keep alive
Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms.");
obj.timer = setInterval(function () {
SendKeepAliveRequest(obj.forwardClient.ws);
}, obj.args.mpskeepalive);//
}
return 5;
}
Debug("APF: Request successful.");
return 1;
}
case APFProtocol.USERAUTH_SUCCESS: {
Debug("APF: User Authentication successful");
// Send Pfwd service request
SendServiceRequest(socket.ws, 'pfwd@amt.intel.com');
return 1;
}
case APFProtocol.USERAUTH_FAILURE: {
Debug("APF: User Authentication failed");
obj.cirastate = CIRASTATE.FAILED;
return 14;
}
case APFProtocol.KEEPALIVE_REQUEST: {
Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1));
SendKeepAliveReply(socket.ws, ReadInt(data, 1));
return 5;
}
case APFProtocol.KEEPALIVE_REPLY: {
Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1));
return 5;
}
// Channel management
case APFProtocol.CHANNEL_OPEN: {
// Parse CHANNEL OPEN request
var p_res = parseChannelOpen(data);
Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res));
// Check if target port is in pfwd_ports
if (pfwd_ports.indexOf(p_res.target_port) >= 0) {
// Connect socket to that port
var chan = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () {
//require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "CHANNEL_OPEN-open" });
// obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting
SendChannelOpenConfirm(socket.ws, p_res);
});
// Setup flow control
chan.maxInWindow = p_res.window_size; // Oddly, we are using the same window size as the other side.
chan.curInWindow = 0;
chan.on('data', function (ddata) {
// Relay data to fordwardclient
// TODO: Implement flow control
SendChannelData(socket.ws, p_res.sender_chan, ddata);
});
chan.on('error', function (e) {
Debug("Downlink connection error: " + e);
});
chan.on('end', function () {
var chan = obj.downlinks[p_res.sender_chan];
if (chan != null) {
Debug("Socket ends.");
try { SendChannelClose(socket.ws, p_res.sender_chan); } catch (ex) { }
delete obj.downlinks[p_res.sender_chan];
}
});
obj.downlinks[p_res.sender_chan] = chan;
} else {
// Not a supported port, fail the connection
SendChannelOpenFailure(socket.ws, p_res);
}
return p_res.len;
}
case APFProtocol.CHANNEL_OPEN_CONFIRMATION: {
Debug("APF: CHANNEL_OPEN_CONFIRMATION");
return 17;
}
case APFProtocol.CHANNEL_CLOSE: {
var rcpt_chan = ReadInt(data, 1);
Debug("APF: CHANNEL_CLOSE: " + rcpt_chan);
try { obj.downlinks[rcpt_chan].end(); } catch (ex) { }
return 5;
}
case APFProtocol.CHANNEL_DATA: {
Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data)));
var rcpt_chan = ReadInt(data, 1);
var chan_data_len = ReadInt(data, 5);
var chan_data = data.substring(9, 9 + chan_data_len);
var chan = obj.downlinks[rcpt_chan];
if (chan != null) {
chan.curInWindow += chan_data_len;
try {
chan.write(Buffer.from(chan_data, 'binary'), function () {
Debug("Write completed.");
// If the incoming window is over half used, send an adjust.
if (this.curInWindow > (this.maxInWindow / 2)) { SendChannelWindowAdjust(socket.ws, rcpt_chan, this.curInWindow); this.curInWindow = 0; }
});
} catch (ex) { Debug("Cannot forward data to downlink socket."); }
}
return 9 + chan_data_len;
}
case APFProtocol.CHANNEL_WINDOW_ADJUST: {
Debug("APF: CHANNEL_WINDOW_ADJUST");
return 9;
}
case APFProtocol.JSON_CONTROL: {
Debug("APF: JSON_CONTROL");
var len = ReadInt(data, 1);
if (obj.onJsonControl) { var o = null; try { o = JSON.parse(data.substring(5, 5 + len)); } catch (ex) { } if (o != null) { obj.onJsonControl(o); } }
return 5 + len;
}
default: {
Debug("CMD: " + cmd + " is not implemented.");
obj.cirastate = CIRASTATE.FAILED;
return 0;
}
}
}
function parseChannelOpen(data) {
var result = { cmd: APFProtocol.CHANNEL_OPEN };
var chan_type_slen = ReadInt(data, 1);
result.chan_type = data.substring(5, 5 + chan_type_slen);
result.sender_chan = ReadInt(data, 5 + chan_type_slen);
result.window_size = ReadInt(data, 9 + chan_type_slen);
var c_len = ReadInt(data, 17 + chan_type_slen);
result.target_address = data.substring(21 + chan_type_slen, 21 + chan_type_slen + c_len);
result.target_port = ReadInt(data, 21 + chan_type_slen + c_len);
var o_len = ReadInt(data, 25 + chan_type_slen + c_len);
result.origin_address = data.substring(29 + chan_type_slen + c_len, 29 + chan_type_slen + c_len + o_len);
result.origin_port = ReadInt(data, 29 + chan_type_slen + c_len + o_len);
result.len = 33 + chan_type_slen + c_len + o_len;
return result;
}
function SendChannelOpenFailure(socket, chan_data) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan) + IntToStr(2) + IntToStr(0) + IntToStr(0));
Debug("APF: Send ChannelOpenFailure");
}
function SendChannelOpenConfirm(socket, chan_data) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF));
Debug("APF: Send ChannelOpenConfirmation");
}
function SendChannelWindowAdjust(socket, chan, size) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + IntToStr(chan) + IntToStr(size));
Debug("APF: Send ChannelWindowAdjust, channel: " + chan + ", size: " + size);
}
function SendChannelData(socket, chan, data) {
socket.write(Buffer.concat([Buffer.from(String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(data.length), 'binary'), data]));
Debug("APF: Send ChannelData: " + data.toString('hex'));
}
function SendChannelClose(socket, chan) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan));
Debug("APF: Send ChannelClose ");
}
obj.connect = function () {
if (obj.forwardClient != null) {
try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); }
//obj.forwardClient = null;
}
obj.cirastate = CIRASTATE.INITIAL;
obj.pfwd_idx = 0;
//obj.forwardClient = new obj.ws(obj.args.mpsurl, obj.tlsoptions);
//obj.forwardClient.on("open", obj.onSecureConnect);
var wsoptions = obj.http.parseUri(obj.args.mpsurl);
wsoptions.rejectUnauthorized = 0;
obj.forwardClient = obj.http.request(wsoptions);
obj.forwardClient.upgrade = obj.onSecureConnect;
obj.forwardClient.end(); // end request, trigger completion of HTTP request
}
obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); } }
return obj;
}
module.exports = CreateAPFClient;

View File

@ -35,6 +35,9 @@ function CreateAPFClient(parent, args) {
obj.pfwd_idx = 0;
obj.timer = null; // Keep alive timer
// obj.onChannelClosed
// obj.onJsonControl
// Function copied from common.js
function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
@ -139,16 +142,14 @@ function CreateAPFClient(parent, args) {
}
obj.onSecureConnect = function onSecureConnect(resp, ws, head) {
//Debug("APF Secure WebSocket connected.");
Debug("APF Secure WebSocket connected.");
//console.log(JSON.stringify(resp));
obj.forwardClient.tag = { accumulator: [] };
obj.forwardClient.ws = ws;
obj.forwardClient.ws.on('end', function () {
//Debug("APF: Connection is closing.");
if (obj.timer != null) {
clearInterval(obj.timer);
obj.timer = null;
}
Debug("APF: Connection is closing.");
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
if (obj.onChannelClosed) { obj.onChannelClosed(obj); }
});
obj.forwardClient.ws.on('data', function (data) {
@ -159,21 +160,16 @@ function CreateAPFClient(parent, args) {
len = ProcessData(obj.forwardClient);
if (len > 0) { obj.forwardClient.tag.accumulator = obj.forwardClient.tag.accumulator.slice(len); }
if (obj.cirastate == CIRASTATE.FAILED) {
//Debug("APF: in a failed state, destroying socket.");
Debug("APF: in a failed state, destroying socket.");
obj.forwardClient.ws.end();
}
} while (len > 0);
} catch (e) {
Debug(e);
}
} catch (ex) { Debug(ex); }
});
obj.forwardClient.ws.on('error', function (e) {
//Debug("APF: Connection error, ending connecting.");
if (obj.timer != null) {
clearInterval(obj.timer);
obj.timer = null;
}
Debug("APF: Connection error, ending connecting.");
if (obj.timer != null) { clearInterval(obj.timer); obj.timer = null; }
});
obj.state = CIRASTATE.INITIAL;
@ -185,20 +181,20 @@ function CreateAPFClient(parent, args) {
function SendJsonControl(socket, o) {
var data = JSON.stringify(o)
socket.write(String.fromCharCode(APFProtocol.JSON_CONTROL) + IntToStr(data.length) + data);
//Debug("APF: Send JSON control: " + data);
Debug("APF: Send JSON control: " + data);
}
function SendProtocolVersion(socket, uuid) {
var data = String.fromCharCode(APFProtocol.PROTOCOLVERSION) + IntToStr(1) + IntToStr(0) + IntToStr(0) + hex2rstr(strToGuid(uuid)) + binzerostring(64);
socket.write(data);
//Debug("APF: Send protocol version 1 0 " + uuid);
Debug("APF: Send protocol version 1 0 " + uuid);
obj.cirastate = CIRASTATE.PROTOCOL_VERSION_SENT;
}
function SendServiceRequest(socket, service) {
var data = String.fromCharCode(APFProtocol.SERVICE_REQUEST) + IntToStr(service.length) + service;
socket.write(data);
//Debug("APF: Send service request " + service);
Debug("APF: Send service request " + service);
if (service == 'auth@amt.intel.com') {
obj.cirastate = CIRASTATE.AUTH_SERVICE_REQUEST_SENT;
} else if (service == 'pfwd@amt.intel.com') {
@ -213,7 +209,7 @@ function CreateAPFClient(parent, args) {
data += IntToStr(8) + 'password';
data += binzerostring(1) + IntToStr(pass.length) + pass;
socket.write(data);
//Debug("APF: Send username password authentication to MPS");
Debug("APF: Send username password authentication to MPS");
obj.cirastate = CIRASTATE.AUTH_REQUEST_SENT;
}
@ -222,18 +218,18 @@ function CreateAPFClient(parent, args) {
var data = String.fromCharCode(APFProtocol.GLOBAL_REQUEST) + IntToStr(tcpipfwd.length) + tcpipfwd + binzerostring(1, 1);
data += IntToStr(amthostname.length) + amthostname + IntToStr(amtport);
socket.write(data);
//Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport);
Debug("APF: Send tcpip-forward " + amthostname + ":" + amtport);
obj.cirastate = CIRASTATE.GLOBAL_REQUEST_SENT;
}
function SendKeepAliveRequest(socket) {
socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REQUEST) + IntToStr(255));
//Debug("APF: Send keepalive request");
Debug("APF: Send keepalive request");
}
function SendKeepAliveReply(socket, cookie) {
socket.write(String.fromCharCode(APFProtocol.KEEPALIVE_REPLY) + IntToStr(cookie));
//Debug("APF: Send keepalive reply");
Debug("APF: Send keepalive reply");
}
function ProcessData(socket) {
@ -246,7 +242,7 @@ function CreateAPFClient(parent, args) {
switch (cmd) {
case APFProtocol.SERVICE_ACCEPT: {
var slen = ReadInt(data, 1), service = data.substring(5, 6 + slen);
//Debug("APF: Service request to " + service + " accepted.");
Debug("APF: Service request to " + service + " accepted.");
if (service == 'auth@amt.intel.com') {
if (obj.cirastate >= CIRASTATE.AUTH_SERVICE_REQUEST_SENT) {
SendUserAuthRequest(socket.ws, obj.args.mpsuser, obj.args.mpspass);
@ -261,47 +257,47 @@ function CreateAPFClient(parent, args) {
case APFProtocol.REQUEST_SUCCESS: {
if (len >= 5) {
var port = ReadInt(data, 1);
//Debug("APF: Request to port forward " + port + " successful.");
Debug("APF: Request to port forward " + port + " successful.");
// iterate to pending port forward request
if (obj.pfwd_idx < pfwd_ports.length) {
SendGlobalRequestPfwd(socket.ws, obj.args.clientname, pfwd_ports[obj.pfwd_idx++]);
} else {
// no more port forward, now setup timer to send keep alive
//Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms.");
Debug("APF: Start keep alive for every " + obj.args.mpskeepalive + " ms.");
obj.timer = setInterval(function () {
SendKeepAliveRequest(obj.forwardClient.ws);
}, obj.args.mpskeepalive);//
}
return 5;
}
//Debug("APF: Request successful.");
Debug("APF: Request successful.");
return 1;
}
case APFProtocol.USERAUTH_SUCCESS: {
//Debug("APF: User Authentication successful");
Debug("APF: User Authentication successful");
// Send Pfwd service request
SendServiceRequest(socket.ws, 'pfwd@amt.intel.com');
return 1;
}
case APFProtocol.USERAUTH_FAILURE: {
//Debug("APF: User Authentication failed");
Debug("APF: User Authentication failed");
obj.cirastate = CIRASTATE.FAILED;
return 14;
}
case APFProtocol.KEEPALIVE_REQUEST: {
//Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1));
Debug("APF: Keep Alive Request with cookie: " + ReadInt(data, 1));
SendKeepAliveReply(socket.ws, ReadInt(data, 1));
return 5;
}
case APFProtocol.KEEPALIVE_REPLY: {
//Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1));
Debug("APF: Keep Alive Reply with cookie: " + ReadInt(data, 1));
return 5;
}
// Channel management
case APFProtocol.CHANNEL_OPEN: {
// Parse CHANNEL OPEN request
var p_res = parseChannelOpen(data);
//Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res));
Debug("APF: CHANNEL_OPEN request: " + JSON.stringify(p_res));
// Check if target port is in pfwd_ports
if (pfwd_ports.indexOf(p_res.target_port) >= 0) {
// Connect socket to that port
@ -322,21 +318,15 @@ function CreateAPFClient(parent, args) {
});
chan.on('error', function (e) {
//Debug("Downlink connection error: " + e);
Debug("Downlink connection error: " + e);
});
chan.on('end', function () {
var chan = obj.downlinks[p_res.sender_chan];
if (chan != null) {
try {
//Debug("Socket ends.");
SendChannelClose(socket.ws, p_res.sender_chan);
chan.xclosed = 1;
// Add some delay before removing... otherwise race condition
setTimeout(function () { delete obj.downlinks[p_res.sender_chan]; }, 100);
} catch (e) {
//Debug("Downlink connection exception: " + e);
}
Debug("Socket ends.");
try { SendChannelClose(socket.ws, p_res.sender_chan); } catch (ex) { }
delete obj.downlinks[p_res.sender_chan];
}
});
@ -348,22 +338,17 @@ function CreateAPFClient(parent, args) {
return p_res.len;
}
case APFProtocol.CHANNEL_OPEN_CONFIRMATION: {
//Debug("APF: CHANNEL_OPEN_CONFIRMATION");
Debug("APF: CHANNEL_OPEN_CONFIRMATION");
return 17;
}
case APFProtocol.CHANNEL_CLOSE: {
var rcpt_chan = ReadInt(data, 1);
//Debug("APF: CHANNEL_CLOSE: " + rcpt_chan);
var chan = obj.downlinks[rcpt_chan];
if ((chan != null) && (chan.xclosed !== 1)) {
SendChannelClose(socket.ws, rcpt_chan);
try { obj.downlinks[rcpt_chan].end(); } catch (e) { }
delete obj.downlinks[rcpt_chan];
}
Debug("APF: CHANNEL_CLOSE: " + rcpt_chan);
try { obj.downlinks[rcpt_chan].end(); } catch (ex) { }
return 5;
}
case APFProtocol.CHANNEL_DATA: {
//Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data)));
Debug("APF: CHANNEL_DATA: " + JSON.stringify(rstr2hex(data)));
var rcpt_chan = ReadInt(data, 1);
var chan_data_len = ReadInt(data, 5);
var chan_data = data.substring(9, 9 + chan_data_len);
@ -372,22 +357,26 @@ function CreateAPFClient(parent, args) {
chan.curInWindow += chan_data_len;
try {
chan.write(Buffer.from(chan_data, 'binary'), function () {
//Debug("Write completed.");
Debug("Write completed.");
// If the incoming window is over half used, send an adjust.
if (this.curInWindow > (this.maxInWindow / 2)) { SendChannelWindowAdjust(socket.ws, rcpt_chan, this.curInWindow); this.curInWindow = 0; }
});
} catch (e) {
//Debug("Cannot forward data to downlink socket.");
}
} catch (ex) { Debug("Cannot forward data to downlink socket."); }
}
return 9 + chan_data_len;
}
case APFProtocol.CHANNEL_WINDOW_ADJUST: {
//Debug("APF: CHANNEL_WINDOW_ADJUST ");
Debug("APF: CHANNEL_WINDOW_ADJUST");
return 9;
}
case APFProtocol.JSON_CONTROL: {
Debug("APF: JSON_CONTROL");
var len = ReadInt(data, 1);
if (obj.onJsonControl) { var o = null; try { o = JSON.parse(data.substring(5, 5 + len)); } catch (ex) { } if (o != null) { obj.onJsonControl(o); } }
return 5 + len;
}
default: {
//Debug("CMD: " + cmd + " is not implemented.");
Debug("CMD: " + cmd + " is not implemented.");
obj.cirastate = CIRASTATE.FAILED;
return 0;
}
@ -412,36 +401,32 @@ function CreateAPFClient(parent, args) {
function SendChannelOpenFailure(socket, chan_data) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_FAILURE) + IntToStr(chan_data.sender_chan) + IntToStr(2) + IntToStr(0) + IntToStr(0));
//Debug("APF: Send ChannelOpenFailure");
Debug("APF: Send ChannelOpenFailure");
}
function SendChannelOpenConfirm(socket, chan_data) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_OPEN_CONFIRMATION) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.sender_chan) + IntToStr(chan_data.window_size) + IntToStr(0xFFFFFFFF));
//Debug("APF: Send ChannelOpenConfirmation");
Debug("APF: Send ChannelOpenConfirmation");
}
function SendChannelWindowAdjust(socket, chan, size) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_WINDOW_ADJUST) + IntToStr(chan) + IntToStr(size));
//Debug("APF: Send ChannelWindowAdjust: " + rstr2hex(data));
Debug("APF: Send ChannelWindowAdjust, channel: " + chan + ", size: " + size);
}
function SendChannelData(socket, chan, data) {
socket.write(Buffer.concat([Buffer.from(String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(data.length), 'binary'), data]));
//Debug("APF: Send ChannelData: " + rstr2hex(buf));
Debug("APF: Send ChannelData: " + data.toString('hex'));
}
function SendChannelClose(socket, chan) {
socket.write(String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan));
//Debug("APF: Send ChannelClose ");
Debug("APF: Send ChannelClose ");
}
obj.connect = function () {
if (obj.forwardClient != null) {
try {
obj.forwardClient.ws.end();
} catch (e) {
Debug(e);
}
try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); }
//obj.forwardClient = null;
}
obj.cirastate = CIRASTATE.INITIAL;
@ -457,7 +442,7 @@ function CreateAPFClient(parent, args) {
obj.forwardClient.end(); // end request, trigger completion of HTTP request
}
obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (e) { Debug(e); } }
obj.disconnect = function () { try { obj.forwardClient.ws.end(); } catch (ex) { Debug(ex); } }
return obj;
}

View File

@ -240,19 +240,17 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, mpsConn
if (state == 2) {
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
var options = { socket: ser, ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: obj.constants.SSL_OP_NO_SSLv2 | obj.constants.SSL_OP_NO_SSLv3 | obj.constants.SSL_OP_NO_COMPRESSION | obj.constants.SSL_OP_CIPHER_SERVER_PREFERENCE, rejectUnauthorized: false };
if (obj.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
if (obj.xtlsMethod == 1) { options.secureProtocol = 'TLSv1_method'; }
if (obj.xtlsoptions) {
if (obj.xtlsoptions.ca) options.ca = obj.xtlsoptions.ca;
if (obj.xtlsoptions.cert) options.cert = obj.xtlsoptions.cert;
if (obj.xtlsoptions.key) options.key = obj.xtlsoptions.key;
}
//obj.socket = new TLSSocket(ser, options);
obj.socket = obj.tls.connect(obj.port, obj.host, options, obj.xxOnSocketConnected);
obj.socket.setEncoding('binary');
obj.socket.setTimeout(6000); // Set socket idle timeout
obj.socket.on('error', function (err) { console.log("CIRA TLS Connection Error ", err); obj.xxOnSocketClosed(); });
//obj.socket.on('error', function (e) { if (e.message && e.message.indexOf('sslv3 alert bad record mac') >= 0) { obj.xtlsMethod = 1 - obj.xtlsMethod; } });
obj.socket.on('error', function (ex) { obj.xtlsMethod = 1 - obj.xtlsMethod; });
obj.socket.on('close', obj.xxOnSocketClosed);
obj.socket.on('timeout', obj.xxOnSocketTimeout);

View File

@ -76,6 +76,9 @@ module.exports.CreateAmtManager = function(parent) {
// Remove the device from the list
devices.splice(i, 1);
if (devices.length == 0) { delete obj.amtDevices[dev.nodeid]; } else { obj.amtDevices[dev.nodeid] = devices; }
// Notify connection closure if this is a LMS connection
if (dev.connType == 2) { dev.controlMsg({ action: "close" }); }
return true;
}
@ -103,9 +106,13 @@ module.exports.CreateAmtManager = function(parent) {
var devices = obj.amtDevices[nodeid], dev = null;
if (devices != null) { for (var i in devices) { if ((devices[i].mpsConnection == connection) || (devices[i].host == connection)) { dev = devices[i]; } } }
if (dev != null) return false; // We are already managing this device on this connection
dev = { nodeid: nodeid, connType: connType };
dev = { nodeid: nodeid, connType: connType, domainid: nodeid.split('/')[1] };
if (typeof connection == 'string') { dev.host = connection; }
if (typeof connection == 'object') { dev.mpsConnection = connection; }
dev.consoleMsg = function deviceConsoleMsg(msg) { if (typeof deviceConsoleMsg.conn == 'object') { deviceConsoleMsg.conn.ControlMsg({ action: 'console', msg: msg }); } }
dev.consoleMsg.conn = connection;
dev.controlMsg = function deviceControlMsg(msg) { if (typeof deviceControlMsg.conn == 'object') { deviceControlMsg.conn.ControlMsg(msg); } }
dev.controlMsg.conn = connection;
parent.debug('amt', "Start Management", nodeid, connType);
addAmtDevice(dev);
fetchIntelAmtInformation(dev);
@ -192,6 +199,7 @@ module.exports.CreateAmtManager = function(parent) {
//if (node.host) { dev.host = node.host.toLowerCase(); }
dev.meshid = node.meshid;
dev.intelamt = node.intelamt;
dev.consoleMsg("Attempting Intel AMT connection...");
attemptInitialContact(dev);
});
}
@ -201,7 +209,7 @@ module.exports.CreateAmtManager = function(parent) {
parent.debug('amt', "Attempt Initial Contact", dev.name, dev.connType);
if ((dev.acctry == null) && ((typeof dev.intelamt.user != 'string') || (typeof dev.intelamt.pass != 'string'))) {
if ((obj.amtAdminAccounts[dev.domainid] != null) && (obj.amtAdminAccounts[dev.domainid].length > 0)) { dev.acctry = 0; } else { return; }
if ((obj.amtAdminAccounts[dev.domainid] != null) && (obj.amtAdminAccounts[dev.domainid].length > 0)) { dev.acctry = 0; } else { removeAmtDevice(dev); return; }
}
switch (dev.connType) {
@ -315,6 +323,7 @@ module.exports.CreateAmtManager = function(parent) {
// Check the response
if ((status == 200) && (responses['AMT_GeneralSettings'] != null) && (responses['IPS_HostBasedSetupService'] != null) && (responses['IPS_HostBasedSetupService'].response != null) && (responses['IPS_HostBasedSetupService'].response != null) && (stack.wsman.comm.digestRealm == responses['AMT_GeneralSettings'].response.DigestRealm)) {
// Everything looks good
dev.consoleMsg(stack.wsman.comm.xtls ? "Intel AMT connected with TLS." : "Intel AMT connected.");
dev.state = 1;
if (dev.aquired == null) { dev.aquired = {}; }
dev.aquired.controlMode = responses['IPS_HostBasedSetupService'].response.CurrentControlMode; // 1 = CCM, 2 = ACM
@ -330,15 +339,20 @@ module.exports.CreateAmtManager = function(parent) {
// Perform Intel AMT clock sync
attemptSyncClock(dev, function () {
attemptFetchHardwareInventory(dev); // See if we need to get hardware inventory
if (dev.connType != 2) {
// Start power polling if not connected to LMS
var ppfunc = function powerPoleFunction() { fetchPowerState(powerPoleFunction.dev); }
ppfunc.dev = dev;
dev.polltimer = new setTimeout(ppfunc, 290000); // Poll for power state every 4 minutes 50 seconds.
fetchPowerState(dev);
}
// See if we need to get hardware inventory
attemptFetchHardwareInventory(dev, function () {
dev.consoleMsg('Done.');
if (dev.connType != 2) {
// Start power polling if not connected to LMS
var ppfunc = function powerPoleFunction() { fetchPowerState(powerPoleFunction.dev); }
ppfunc.dev = dev;
dev.polltimer = new setTimeout(ppfunc, 290000); // Poll for power state every 4 minutes 50 seconds.
fetchPowerState(dev);
} else {
// For LMS connections, close now.
dev.controlMsg({ action: "close" });
}
});
});
} else {
// We got a bad response
@ -509,7 +523,8 @@ module.exports.CreateAmtManager = function(parent) {
// Care should be take not to have many pending WSMAN called when performing clock sync.
function attemptSyncClock(dev, func) {
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
dev.clockSyncCompleted = func;
dev.taskCount = 1;
dev.taskCompleted = func;
dev.amtstack.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch(attemptSyncClockEx);
}
@ -517,17 +532,19 @@ module.exports.CreateAmtManager = function(parent) {
function attemptSyncClockEx(stack, name, response, status) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
if (status != 200) { removeDevice(dev.nodeid); }
if (status != 200) { removeDevice(dev.nodeid); return; }
// Compute how much drift between Intel AMT and our clock.
var t = new Date(), now = new Date();
t.setTime(response.Body['Ta0'] * 1000);
if (Math.abs(t - now) > 10000) { // If the Intel AMT clock is more than 10 seconds off, set it.
dev.consoleMsg("Performing clock sync.");
var Tm1 = Math.round(now.getTime() / 1000);
dev.amtstack.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch(response.Body['Ta0'], Tm1, Tm1, attemptSyncClockSet);
} else {
// Clock is fine, we are done.
if (dev.clockSyncCompleted != null) { var f = dev.clockSyncCompleted; delete dev.clockSyncCompleted; f(); }
dev.consoleMsg("Clock ok.");
devTaskCompleted(dev)
}
}
@ -536,28 +553,37 @@ module.exports.CreateAmtManager = function(parent) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
if (status != 200) { removeDevice(dev.nodeid); }
if (dev.clockSyncCompleted != null) { var f = dev.clockSyncCompleted; delete dev.clockSyncCompleted; f(); }
devTaskCompleted(dev)
}
function attemptFetchHardwareInventory(dev) {
if (obj.amtDevices[dev.nodeid] == null) return false; // Device no longer exists, ignore this request.
function attemptFetchHardwareInventory(dev, func) {
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
const mesh = parent.webserver.meshes[dev.meshid];
if (mesh == null) { removeDevice(dev.nodeid); return false; }
if (mesh == null) { removeDevice(dev.nodeid); return; }
if (mesh.mtype == 1) { // If this is a Intel AMT only device group, pull the hardware inventory and network information for this device
dev.consoleMsg("Fetching hardware inventory.");
dev.taskCount = 2;
dev.taskCompleted = func;
dev.amtstack.BatchEnum('', ['*CIM_ComputerSystemPackage', 'CIM_SystemPackaging', '*CIM_Chassis', 'CIM_Chip', '*CIM_Card', '*CIM_BIOSElement', 'CIM_Processor', 'CIM_PhysicalMemory', 'CIM_MediaAccessDevice', 'CIM_PhysicalPackage'], attemptFetchHardwareInventoryResponse);
dev.amtstack.BatchEnum('', ['AMT_EthernetPortSettings'], attemptFetchNetworkResponse);
return true;
} else {
if (func) { func(); }
}
return false;
}
//
function devTaskCompleted(dev) {
dev.taskCount--;
if (dev.taskCount == 0) { var f = dev.taskCompleted; delete dev.taskCount; delete dev.taskCompleted; if (f != null) { f(); } }
}
function attemptFetchNetworkResponse(stack, name, responses, status) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
if (status != 200) return;
if (status != 200) { devTaskCompleted(dev); return; }
//console.log(JSON.stringify(responses, null, 2));
if ((responses['AMT_EthernetPortSettings'] == null) || (responses['AMT_EthernetPortSettings'].responses == null)) return;
if ((responses['AMT_EthernetPortSettings'] == null) || (responses['AMT_EthernetPortSettings'].responses == null)) { devTaskCompleted(dev); return; }
// Find the wired and wireless interfaces
var wired = null, wireless = null;
@ -567,7 +593,7 @@ module.exports.CreateAmtManager = function(parent) {
if (netif.WLANLinkProtectionLevel != null) { wireless = netif; } else { wired = netif; }
}
}
if ((wired == null) && (wireless == null)) return;
if ((wired == null) && (wireless == null)) { devTaskCompleted(dev); return; }
// Sent by the agent to update agent network interface information
var net = { netif2: {} };
@ -601,6 +627,8 @@ module.exports.CreateAmtManager = function(parent) {
// Event the node interface information change
parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, { action: 'ifchange', nodeid: dev.nodeid, domain: dev.nodeid.split('/')[1], nolog: 1 });
devTaskCompleted(dev);
}
@ -625,7 +653,7 @@ module.exports.CreateAmtManager = function(parent) {
function attemptFetchHardwareInventoryResponse(stack, name, responses, status) {
const dev = stack.dev;
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
if (status != 200) return;
if (status != 200) { devTaskCompleted(dev); return; }
// Extract basic data
var hw = {}
@ -648,9 +676,11 @@ module.exports.CreateAmtManager = function(parent) {
var m2 = {}, m = hw.PhysicalMemory[i];
m2.BankLabel = m.BankLabel;
m2.Capacity = m.Capacity;
m2.PartNumber = m.PartNumber.trim();
m2.SerialNumber = m.SerialNumber.trim();
m2.Manufacturer = m.Manufacturer.trim();
if (m.PartNumber) { m2.PartNumber = m.PartNumber.trim(); }
if (typeof m.SerialNumber == 'string') { m2.SerialNumber = m.SerialNumber.trim(); }
if (typeof m.SerialNumber == 'number') { m2.SerialNumber = m.SerialNumber; }
if (typeof m.SerialNumber == 'string') { m2.Manufacturer = m.Manufacturer.trim(); }
if (typeof m.Manufacturer == 'number') { m2.Manufacturer = m.Manufacturer; }
memory.push(m2);
}
hw2.hardware.windows.memory = memory;
@ -660,21 +690,21 @@ module.exports.CreateAmtManager = function(parent) {
for (var i in hw.MediaAccessDevice) {
var m2 = {}, m = hw.MediaAccessDevice[i];
m2.Caption = m.DeviceID;
m2.Size = (m.MaxMediaSize * 1000);
if (m.MaxMediaSize) { m2.Size = (m.MaxMediaSize * 1000); }
drives.push(m2);
}
hw2.hardware.identifiers.storage_devices = drives;
}
if (hw.Bios != null) {
hw2.hardware.identifiers.bios_vendor = hw.Bios.Manufacturer.trim();
if (hw.Bios.Manufacturer) { hw2.hardware.identifiers.bios_vendor = hw.Bios.Manufacturer.trim(); }
hw2.hardware.identifiers.bios_version = hw.Bios.Version;
if (hw.Bios.ReleaseDate && hw.Bios.ReleaseDate.Datetime) { hw2.hardware.identifiers.bios_date = hw.Bios.ReleaseDate.Datetime; }
}
if (hw.PhysicalPackage != null) {
hw2.hardware.identifiers.board_name = hw.Card.Model.trim();
hw2.hardware.identifiers.board_vendor = hw.Card.Manufacturer.trim();
hw2.hardware.identifiers.board_version = hw.Card.Version.trim();
hw2.hardware.identifiers.board_serial = hw.Card.SerialNumber.trim();
if (hw.Card.Model) { hw2.hardware.identifiers.board_name = hw.Card.Model.trim(); }
if (hw.Card.Manufacturer) { hw2.hardware.identifiers.board_vendor = hw.Card.Manufacturer.trim(); }
if (hw.Card.Version) { hw2.hardware.identifiers.board_version = hw.Card.Version.trim(); }
if (hw.Card.SerialNumber) { hw2.hardware.identifiers.board_serial = hw.Card.SerialNumber.trim(); }
}
if ((hw.Chips != null) && (hw.Chips.length > 0)) {
for (var i in hw.Chips) {
@ -704,6 +734,8 @@ module.exports.CreateAmtManager = function(parent) {
parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, event);
}
});
devTaskCompleted(dev);
}
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + '-' + g.substring(10, 12) + g.substring(8, 10) + '-' + g.substring(14, 16) + g.substring(12, 14) + '-' + g.substring(16, 20) + '-' + g.substring(20); }

View File

@ -285,6 +285,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
socket.SetupChannel.parent = obj;
socket.SetupChannel.conn = socket;
socket.websocket = 1;
socket.ControlMsg = function ControlMsg(message) { return ControlMsg.parent.SendJsonControl(ControlMsg.conn, message); }
socket.ControlMsg.parent = obj;
socket.ControlMsg.conn = socket;
parent.debug('mps', "New CIRA websocket connection");
socket.on('message', function (data) {
@ -913,6 +916,13 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
for (var i in connections) { obj.close(connections[i]); }
};
obj.SendJsonControl = function(socket, data) {
if (socket.tag.connType == 0) return; // This command is valid only for connections that are not really CIRA.
parent.debug('mpscmd', '<-- JSON_CONTROL');
if (typeof data == 'object') { data = JSON.stringify(data); }
Write(socket, String.fromCharCode(APFProtocol.JSON_CONTROL) + common.IntToStr(data.length) + data);
}
function SendServiceAccept(socket, service) {
parent.debug('mpscmd', '<-- SERVICE_ACCEPT', service);
Write(socket, String.fromCharCode(APFProtocol.SERVICE_ACCEPT) + common.IntToStr(service.length) + service);

View File

@ -3520,6 +3520,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); delete ws.logfile; }, ws); }
});
// Note that here, req.query.p: 1 = WSMAN with server auth, 2 = REDIR with server auth, 3 = WSMAN without server auth, 4 = REDIR with server auth
// Fetch Intel AMT credentials & Setup interceptor
if (req.query.p == 1) {
parent.debug('webrelaydata', 'INTERCEPTOR1', { host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass });
@ -3587,7 +3589,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Compute target port
var port = 16992;
if (node.intelamt.tls > 0) port = 16993; // This is a direct connection, use TLS when possible
if (req.query.p == 2) port += 2;
if ((req.query.p == 2) || (req.query.p == 4)) port += 2;
if (node.intelamt.tls == 0) {
// If this is TCP (without TLS) set a normal TCP socket