Started work on Intel AMT Click Once Recovery.

Ylian Saint-Hilaire committed Mar 22, 2021 at 18:45 UTC a3f85bd0423c93e38038f7b649dc73afc4c2b669
3 files changed +81 -4
meshuser.js
+4
@@ -5492,6 +5492,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5492 }
5493 break;
5494 }
5495 + case 'clickoncerecovery': {
5496 + console.log(command); // TODO
5497 + break;
5498 + }
5499 default: {
5500 // Unknown user action
5501 console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');
views/default.handlebars
+37 -4
@@ -1169,6 +1169,7 @@
1169 <div>Upload File</div>
1170 <form id=d3localmodeform method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
1171 <input type=text id=d3auth name=auth style="display:none" />
1172 + <input type=text id=d3filter name=filter style="display:none" />
1173 <input type=text id=d3attrib name=attrib style="display:none" />
1174 <input type=file id=d3localFile name=files onchange=d3setActions() />
1175 <input type=submit id=d3submit style="display:none" />
@@ -6612,6 +6613,7 @@
6613 if (((currentNode.conn & 1) != 0) && ((rights & 131072) != 0)) { count++; y += '<option value=106>' + "Run Commands" + '</option>'; } // Remote command permission
6614 if ((currentNode.conn != 0) && ((rights & 262144) != 0)) { count++; y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; }
6615 if ((currentNode.conn & 16) != 0) { count++; y += '<option value=103>' + "Send MQTT Message" + '</option>'; }
6616 + if ((getNodeAmtVersion(currentNode) >= 15) && ((currentNode.conn & 6) != 0) && (rights == 0xFFFFFFFF) && ((features & 0x00000400) == 0)) { count++; y += '<option value=107>' + "Intel&reg; AMT Click Once Recovery" + '</option>'; } // CIRA (2) or AMT (4) connected
6617 if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { count++; y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
6618 y += '</select>';
6619 x += addHtmlValue("Operation", y);
@@ -6648,12 +6650,32 @@
6650 Q('d2runcmd').focus();
6651 //QE('idx_dlgOkButton', true);
6652 }
6653 + } else if (op == 107) {
6654 + // Intel AMT Click Once Recovery (OCR)
6655 + Q('d3localmodeform').action = 'clickoncerecovery.ashx';
6656 + Q('d3auth').value = authCookie;
6657 + Q('d3filter').value = '.iso';
6658 + Q('d3attrib').value = currentNode._id;
6659 + setDialogMode(3, "Intel&reg; AMT Click Once Recovery", 3, deviceActionClickOnceRecovery);
6660 + d3init();
6661 } else {
6662 // Power operation
6663 meshserver.send({ action: 'poweraction', nodeids: [ currentNode._id ], actiontype: parseInt(op) });
6664 }
6665 }
6666
6667 + function deviceActionClickOnceRecovery() {
6668 + var mode = Q('d3uploadMode').value;
6669 + if (mode == 1) {
6670 + // Boot using local disk image file
6671 + Q('d3submit').click();
6672 + } else {
6673 + // Boot using server image file
6674 + var files = d3getFileSel();
6675 + if (files.length == 1) { meshserver.send({ action: 'clickoncerecovery', nodeids: [ Q('d3attrib').value ], type: 'diskimage', path: d3filetreelocation.join('/') + '/' + files[0] }); }
6676 + }
6677 + }
6678 +
6679 function deviceRunCmdsFunctionEx() {
6680 var type = 3;
6681 try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
@@ -9664,6 +9686,7 @@
9686 if (xxdialogMode) return;
9687 Q('d3localmodeform').action = 'uploadmeshcorefile.ashx';
9688 Q('d3auth').value = authCookie;
9689 + Q('d3filter').value = '.js';
9690 Q('d3attrib').value = currentNode._id;
9691 setDialogMode(3, "Upload Mesh Agent Core", 3, p15uploadCoreEx2);
9692 d3init();
@@ -11323,7 +11346,7 @@
11346 } else {
11347 var link = shortname, publiclink = '', loginkey = (urlargs.key)?('&key=' + urlargs.key):'';
11348 if (publicfolder) { publiclink = '<img src="images/link2.png" style=cursor:pointer title="' + "Display public link" + '" onclick=\'return p5showPublicLink("' + (publicPath + '/' + encodeURIComponentEx(f.nx)) + '?download=1' + loginkey + '")\' width=10 height=10 /> <img src="images/link4.png" title="' + "Copy link to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(publicPath + '/' + encodeURIComponentEx(f.nx) + '?download=1' + loginkey) + '") width=10 height=10>'; }
11326 - if (f.s > 0) { link = publiclink + ' <a onclick=downloadFile("downloadfile.ashx?link=' + encodeURIComponentEx(filetreelinkpath + '/' + f.nx) + '")>' + shortname + '</a>'; }
11349 + if (f.s > 0) { link = publiclink + '<a onclick=downloadFile("downloadfile.ashx?link=' + encodeURIComponentEx(filetreelinkpath + '/' + f.nx) + '")>' + shortname + '</a>'; }
11350 h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + f.nx + '">&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
11351 }
11352
@@ -13521,6 +13544,7 @@
13544
13545 function d3init() {
13546 Q('d3localFile').value = '';
13547 + Q('d3localFile').accept = Q('d3filter').value;
13548 d3modechange();
13549 }
13550
@@ -13536,7 +13560,7 @@
13560
13561 function d3updatefiles() {
13562 if (Q('d3uploadMode').value == 1) return;
13539 - var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1;
13563 + var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null;
13564
13565 // Navigate to path location, build the paths at the same time
13566 var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
@@ -13564,10 +13588,17 @@
13588 // Sort the files
13589 var filetreexx = p5sort_files(filetreex.f);
13590
13591 + // File filter
13592 + var fileFilter = Q('d3filter').value
13593 +
13594 // Display all files and folders at this location
13595 for (var i in filetreexx) {
13596 // Figure out the name and shortname
13597 var f = filetreexx[i], name = f.n, shortname;
13598 +
13599 + // Filter out files
13600 + if ((f.t == 3) && (fileFilter != '') && (f.nx.toLowerCase().endsWith(fileFilter) == false)) { continue; }
13601 +
13602 shortname = name;
13603 if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + ("..." + '</span>'); } else { shortname = EscapeHtml(name); }
13604 name = EscapeHtml(name);
@@ -13577,7 +13608,7 @@
13608 if (f.s != null) { fsize = getFileSizeStr(f.s); }
13609
13610 var h = '';
13580 - if (f.t < 3) {
13611 + if (f.t != 3) {
13612 var title = '';
13613 h = '<div class=filelist file=999><span style=float:right title="' + title + '"></span><span><div class=fileIcon' + f.t + ' onclick=d3folderset("' + encodeURIComponentEx(f.nx) + '")></div>&nbsp;<a href=# style=cursor:pointer onclick=\'return d3folderset("' + encodeURIComponentEx(f.nx) + '")\'>' + shortname + '</a></span></div>';
13614 } else {
@@ -13619,7 +13650,7 @@
13650 drawNotifications();
13651 }
13652
13622 - // Set the notification count on the upper right oft he screen
13653 + // Set the notification count on the upper right of the screen
13654 function setNotificationCount(c) {
13655 if (parseInt(Q('notificationCount').innerHTML) == c) return; // If the count did not change, exit now.
13656 QH('notificationCount', c);
@@ -14727,6 +14758,8 @@
14758 meshserver.send({ action: 'userWebState', state: JSON.stringify(s) });
14759 }
14760 }
14761 +
14762 + function getNodeAmtVersion(node) { if ((node == null) || (node.intelamt == null) || (typeof node.intelamt.ver != 'string')) return 0; var verSplit = node.intelamt.ver.split('.'); if (verSplit.length < 2) return 0; return parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100); }
14763 function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
14764 function addLink(x, f) { return '<span tabindex=0 style=cursor:pointer;text-decoration:none onclick=\'' + f + '\' onkeypress="if (event.key==\'Enter\') {' + f + '} ">' + x + ' <img class=hoverButton src=images/link5.png></span>'; }
14765 function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
webserver.js
+40
@@ -3407,6 +3407,45 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3407 });
3408 }
3409
3410 + // Upload a MeshCore.js file to the server
3411 + function handleClickOnceRecoveryFile(req, res) {
3412 + const domain = checkUserIpAddress(req, res);
3413 + if (domain == null) { return; }
3414 + if (domain.id !== '') { res.sendStatus(401); return; }
3415 +
3416 + var authUserid = null;
3417 + if ((req.session != null) && (typeof req.session.userid == 'string')) { authUserid = req.session.userid; }
3418 +
3419 + const multiparty = require('multiparty');
3420 + const form = new multiparty.Form();
3421 + form.parse(req, function (err, fields, files) {
3422 + // If an authentication cookie is embedded in the form, use that.
3423 + if ((fields != null) && (fields.auth != null) && (fields.auth.length == 1) && (typeof fields.auth[0] == 'string')) {
3424 + var loginCookie = obj.parent.decodeCookie(fields.auth[0], obj.parent.loginCookieEncryptionKey, 60); // 60 minute timeout
3425 + if ((loginCookie != null) && (obj.args.cookieipcheck !== false) && (loginCookie.ip != null) && (loginCookie.ip != req.clientIp)) { loginCookie = null; } // Check cookie IP binding.
3426 + if ((loginCookie != null) && (domain.id == loginCookie.domainid)) { authUserid = loginCookie.userid; } // Use cookie authentication
3427 + }
3428 + if (authUserid == null) { res.sendStatus(401); return; }
3429 + if ((fields == null) || (fields.attrib == null) || (fields.attrib.length != 1)) { res.sendStatus(404); return; }
3430 +
3431 + // Get the user
3432 + const user = obj.users[authUserid];
3433 + if (user == null) { res.sendStatus(401); return; } // Check this user exists
3434 +
3435 + // Get the node and check node rights
3436 + const nodeid = fields.attrib[0];
3437 + obj.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) {
3438 + if ((node == null) || (rights != 0xFFFFFFFF) || (visible == false)) { res.sendStatus(404); return; } // We don't have remote control rights to this device
3439 + for (var i in files.files) {
3440 + var file = files.files[i];
3441 + console.log('ClickOnceRecovery', file); // TODO
3442 + try { obj.fs.unlinkSync(file.path); } catch (e) { }
3443 + }
3444 + res.send('');
3445 + });
3446 + });
3447 + }
3448 +
3449 // Upload a file to the server
3450 function handleUploadFile(req, res) {
3451 const domain = checkUserIpAddress(req, res);
@@ -5208,6 +5247,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
5247 obj.app.post(url + 'uploadfile.ashx', handleUploadFile);
5248 obj.app.post(url + 'uploadfilebatch.ashx', handleUploadFileBatch);
5249 obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);
5250 + obj.app.post(url + 'clickoncerecovery.ashx', handleClickOnceRecoveryFile);
5251 obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
5252 obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
5253 obj.app.ws(url + 'apf.ashx', function (ws, req) { obj.parent.mpsserver.onWebSocketConnection(ws, req); })