mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-24 06:05:53 -05:00
A working serverside AMT API via 3 different
modes, namely mode 1 (AMT Direct), mode 2 (CIRA) and mode 3 (APF bridge)
This commit is contained in:
parent
d4c55680e2
commit
9c09a3ebc0
@ -332,7 +332,7 @@ function CreateAPFClient(parent, args) {
|
||||
if (pfwd_ports.indexOf(p_res.target_port) >= 0) {
|
||||
// connect socket to that port
|
||||
obj.downlinks[p_res.sender_chan] = obj.net.createConnection({ host: obj.args.clientaddress, port: p_res.target_port }, function () {
|
||||
obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting
|
||||
//obj.downlinks[p_res.sender_chan].setEncoding('binary');//assume everything is binary, not interpreting
|
||||
SendChannelOpenConfirm(socket.ws, p_res);
|
||||
});
|
||||
|
||||
@ -348,8 +348,10 @@ function CreateAPFClient(parent, args) {
|
||||
obj.downlinks[p_res.sender_chan].on('end', function () {
|
||||
if (obj.downlinks[p_res.sender_chan]) {
|
||||
try {
|
||||
Debug("Socket ends.");
|
||||
SendChannelClose(socket.ws, p_res.sender_chan);
|
||||
delete obj.downlinks[p_res.sender_chan];
|
||||
// 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);
|
||||
}
|
||||
@ -449,14 +451,14 @@ function CreateAPFClient(parent, args) {
|
||||
|
||||
function SendChannelData(socket, chan, len, data) {
|
||||
var buf = String.fromCharCode(APFProtocol.CHANNEL_DATA) + IntToStr(chan) + IntToStr(len) + data;
|
||||
socket.write(Buffer.from(buf, 'binary'));
|
||||
socket.write(buf);
|
||||
Debug("APF: Send ChannelData: " + rstr2hex(buf));
|
||||
}
|
||||
|
||||
function SendChannelClose(socket, chan) {
|
||||
var buf = String.fromCharCode(APFProtocol.CHANNEL_CLOSE) + IntToStr(chan);
|
||||
socket.write(Buffer.from(buf, 'binary'));
|
||||
Debug("APF: Send ChannelClose: " + rstr2hex(buf));
|
||||
socket.write(buf);
|
||||
Debug("APF: Send ChannelClose ");
|
||||
}
|
||||
|
||||
obj.connect = function () {
|
||||
|
2
agents/modules_meshcore_min/apfclient.min.js
vendored
2
agents/modules_meshcore_min/apfclient.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @description Intel(r) AMT WSMAN communication using Node.js TLS
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @author Ylian Saint-Hilaire/Joko Sastriawan
|
||||
* @version v0.2.0b
|
||||
*/
|
||||
|
||||
@ -39,7 +39,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
obj.xtls = tls;
|
||||
obj.xtlsoptions = tlsoptions;
|
||||
obj.parent = parent;
|
||||
obj.mode = mode;//0: webrelay; 1: direct, 2: CIRA, 3: APF relay
|
||||
obj.mode = mode;//1: direct, 2: CIRA, 3: APF relay
|
||||
obj.xtlsFingerprint;
|
||||
obj.xtlsCertificate = null;
|
||||
obj.xtlsCheck = 0; // 0 = No TLS, 1 = CA Checked, 2 = Pinned, 3 = Untrusted
|
||||
@ -167,17 +167,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
obj.socketState = 1;
|
||||
obj.kerberosDone = 0;
|
||||
|
||||
if (obj.mode==0 && obj.xtlsoptions && obj.xtlsoptions.meshServerConnect) { //Webrelay
|
||||
// Use the websocket wrapper to connect to MeshServer server
|
||||
obj.socket = CreateWebSocketWrapper(obj.xtlsoptions.host, obj.xtlsoptions.port, '/webrelay.ashx?user=' + encodeURIComponent(obj.xtlsoptions.username) + '&pass=' + encodeURIComponent(obj.xtlsoptions.password) + '&host=' + encodeURIComponent(obj.host) + '&p=1&tls1only=' + obj.xtlsMethod, obj.xtlsoptions.xtlsFingerprint);
|
||||
obj.socket.setEncoding('binary');
|
||||
obj.socket.setTimeout(6000); // Set socket idle timeout
|
||||
obj.socket.ondata = obj.xxOnSocketData;
|
||||
obj.socket.onclose = function () { if (obj.xtlsDataReceived == false) { obj.xtlsMethod = 1 - obj.xtlsMethod; } obj.xxOnSocketClosed(); }
|
||||
obj.socket.ontimeout = function () { if (obj.xtlsDataReceived == false) { obj.xtlsMethod = 1 - obj.xtlsMethod; } obj.xxOnSocketClosed(); }
|
||||
obj.socket.connect(obj.xxOnSocketConnected);
|
||||
obj.socket.setNoDelay(true); // Disable nagle. We will encode each WSMAN request as a single send block and want to send it at once. This may help Intel AMT handle pipelining?
|
||||
} else if (obj.mode==1 ) { //Direct
|
||||
if (obj.mode==1 ) { //Direct
|
||||
if (obj.xtls != 1) {
|
||||
// Connect without TLS
|
||||
obj.socket = new obj.net.Socket();
|
||||
@ -214,7 +204,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
obj.socket = obj.parent.apfserver.SetupCiraChannel(apfconn, obj.port);
|
||||
}
|
||||
obj.socket.onData = function (ccon, data) {
|
||||
_OnSocketData(data);
|
||||
obj.xxOnSocketData(data);
|
||||
}
|
||||
|
||||
obj.socket.onStateChange = function (ccon, state) {
|
||||
@ -225,11 +215,11 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
obj.socketHeader = null;
|
||||
obj.socketData = '';
|
||||
obj.socketState = 0;
|
||||
_OnSocketClosed();
|
||||
obj.xxOnSocketClosed();
|
||||
} catch (e) { }
|
||||
} else if (state == 2) {
|
||||
// channel open success
|
||||
_OnSocketConnected();
|
||||
obj.xxOnSocketConnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,8 +279,8 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
|
||||
// NODE.js specific private method
|
||||
obj.xxOnSocketData = function (data) {
|
||||
obj.xtlsDataReceived = true;
|
||||
if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-RECV(" + data.length + "): " + data); }
|
||||
//console.log("RECV:"+data);
|
||||
obj.xtlsDataReceived = true;
|
||||
if (typeof data === 'object') {
|
||||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||||
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
|
||||
@ -305,7 +295,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
if (obj.socketParseState == 0) {
|
||||
var headersize = obj.socketAccumulator.indexOf("\r\n\r\n");
|
||||
if (headersize < 0) return;
|
||||
//obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
|
||||
//obj.Debug("Header: "+obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
|
||||
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split("\r\n");
|
||||
if (obj.amtVersion == null) { for (var i in obj.socketHeader) { if (obj.socketHeader[i].indexOf('Server: Intel(R) Active Management Technology ') == 0) { obj.amtVersion = obj.socketHeader[i].substring(46); } } }
|
||||
obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
|
||||
@ -361,8 +351,10 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
var s = parseInt(header.Directive[1]);
|
||||
if (isNaN(s)) s = 500;
|
||||
if (s == 401 && ++(obj.authcounter) < 3) {
|
||||
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
|
||||
obj.socket.end();
|
||||
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
|
||||
if (obj.mode==1) {
|
||||
obj.socket.end();
|
||||
}
|
||||
} else {
|
||||
var r = obj.pendingAjaxCall.shift();
|
||||
if (r == null || r.length < 1) { console.log("pendingAjaxCall error, " + r); return; }
|
||||
@ -378,7 +370,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
obj.xxOnSocketClosed = function (data) {
|
||||
//obj.Debug("xxOnSocketClosed");
|
||||
obj.socketState = 0;
|
||||
if (obj.socket != null) { obj.socket.destroy(); obj.socket = null; }
|
||||
if (obj.mode ==1 && obj.socket != null) { obj.socket.destroy(); obj.socket = null; }
|
||||
if (obj.pendingAjaxCall.length > 0) {
|
||||
var r = obj.pendingAjaxCall.shift();
|
||||
var retry = r[5];
|
||||
@ -389,8 +381,7 @@ var CreateWsmanComm = function (host, port, user, pass, tls, tlsoptions, parent,
|
||||
// NODE.js specific private method
|
||||
obj.xxSend = function (x) {
|
||||
if (obj.socketState == 2) {
|
||||
if (urlvars && urlvars['wsmantrace']) { console.log("WSMAN-SEND(" + x.length + "): " + x); }
|
||||
obj.socket.write(new Buffer(x, "binary"));
|
||||
obj.socket.write(Buffer.from(x, "binary"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,12 @@ limitations under the License.
|
||||
|
||||
/**
|
||||
* @description Intel(r) AMT WSMAN Stack
|
||||
* @author Ylian Saint-Hilaire
|
||||
* @author Ylian Saint-Hilaire/Joko Sastriawan
|
||||
* @version v0.2.0
|
||||
*/
|
||||
|
||||
// Construct a MeshServer object
|
||||
function WsmanStackCreateService(CreateWsmanComm, host, port, user, pass, tls, extra)
|
||||
function WsmanStackCreateService(CreateWsmanComm, host, port, user, pass, tls, extra, parent, mode)
|
||||
{
|
||||
var obj = {_ObjectID: 'WSMAN'};
|
||||
//obj.onDebugMessage = null; // Set to a function if you want to get debug messages.
|
||||
@ -38,7 +38,7 @@ function WsmanStackCreateService(CreateWsmanComm, host, port, user, pass, tls, e
|
||||
{
|
||||
var CreateWsmanComm = arguments[0];
|
||||
if (CreateWsmanComm) {
|
||||
obj.comm = new CreateWsmanComm(host, port, user, pass, tls, extra);
|
||||
obj.comm = new CreateWsmanComm(host, port, user, pass, tls, extra, parent, mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
66
meshuser.js
66
meshuser.js
@ -48,6 +48,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
obj.user = user;
|
||||
obj.domain = domain;
|
||||
|
||||
// Server side amt stack
|
||||
var WsmanComm = require('./amt/amt-wsman-comm.js');
|
||||
var Wsman = require('./amt/amt-wsman.js');
|
||||
var Amt = require('./amt/amt.js');
|
||||
|
||||
// Send a message to the user
|
||||
//obj.send = function (data) { try { if (typeof data == 'string') { ws.send(Buffer.from(data, 'binary')); } else { ws.send(data); } } catch (e) { } }
|
||||
|
||||
@ -2784,6 +2789,39 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'amt': {
|
||||
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
|
||||
if (common.validateInt(command.mode, 0, 3) == false) break; // Check connection mode
|
||||
// validate if communication mode is possible
|
||||
if (command.mode == null || command.mode==0) {
|
||||
break;//unsupported
|
||||
} else if (command.mode == 1) {
|
||||
var state = parent.parent.GetConnectivityState(command.nodeid);
|
||||
if ( (state == null) || (state.connectivity & 4)==0 ) break;
|
||||
} else if (command.mode == 2) {
|
||||
if (parent.parent.mpsserver.ciraConnections[command.nodeid] == null) break;
|
||||
} else if (command.mode == 3) {
|
||||
if (parent.parent.apfserver.apfConnections[command.nodeid] == null) break;
|
||||
}
|
||||
var nodeid = command.nodeid;
|
||||
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
|
||||
// Get the device
|
||||
db.Get(nodeid, function (err, nodes) {
|
||||
if ((nodes == null) || (nodes.length != 1)) return;
|
||||
var node = nodes[0];
|
||||
|
||||
// Get the mesh for this device
|
||||
mesh = parent.meshes[node.meshid];
|
||||
if (mesh) {
|
||||
// Check if this user has rights to do this
|
||||
if (mesh.links[user._id] != null && ((mesh.links[user._id].rights & 8) != 0)) { // "Remote Control permission"
|
||||
handleAmtCommand(command, node);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Unknown user action
|
||||
console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');
|
||||
@ -2912,5 +2950,33 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
|
||||
function getRandomPassword() { return Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); }
|
||||
|
||||
function handleAmtCommand(cmd, node) {
|
||||
if (cmd==null) return;
|
||||
var host = cmd.nodeid;
|
||||
if (cmd.mode==1) {
|
||||
host = node.host;
|
||||
}
|
||||
var tlsoptions = null;
|
||||
var wsman = new Wsman(WsmanComm, host, node.intelamt.tls? 16993: 16992, node.intelamt.user, node.intelamt.pass,
|
||||
node.intelamt.tls,tlsoptions, parent.parent, cmd.mode);
|
||||
var amt = new Amt(wsman);
|
||||
switch (cmd.command) {
|
||||
case "Get-GeneralSettings": {
|
||||
amt.Get("AMT_GeneralSettings", function(obj, name, response, status) {
|
||||
if (status==200) {
|
||||
var resp = { action: 'amt', nodeid: cmd.nodeid, command: 'Get-GeneralSettings', value: response.Body}
|
||||
ws.send(JSON.stringify(resp));
|
||||
} else {
|
||||
ws.send(JSON.stringify({"error": error}));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
@ -791,8 +791,8 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
|
||||
return true;
|
||||
}
|
||||
// Send a part of the message
|
||||
cirachannel.sendBuffer = data.substring(cirachannel.sendcredits);
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, data.substring(0, cirachannel.sendcredits));
|
||||
cirachannel.sendBuffer = data.toString('binary').substring(cirachannel.sendcredits);
|
||||
SendChannelData(cirachannel.socket, cirachannel.amtchannelid, data.toString('binary').substring(0, cirachannel.sendcredits));
|
||||
cirachannel.sendcredits = 0;
|
||||
return false;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user