diff --git a/CoreModule.js b/CoreModule.js index af28464d3..2dcf1f781 100644 --- a/CoreModule.js +++ b/CoreModule.js @@ -719,6 +719,43 @@ var http = require('http'); var net = require('net'); var fs = require('fs'); var rtc = require('ILibWebRTC'); + +// OpenFrame: Read machine ID from shared location +var openframeMachineId = null; +function getOpenFrameMachineId() { + if (openframeMachineId != null) return openframeMachineId; + try { + var machineIdPath = (process.platform == 'win32') + ? (process.env['ProgramData'] + '\\OpenFrame\\machine_id') + : ((process.platform == 'darwin') + ? '/Library/Application Support/OpenFrame/machine_id' + : '/var/lib/openframe/machine_id'); + openframeMachineId = fs.readFileSync(machineIdPath).toString().trim(); + } catch (ex) { openframeMachineId = null; } + return openframeMachineId; +} + +// OpenFrame: Add x-machine-id and Authorization headers to request options (only in openFrameMode) +function addOpenFrameHeaders(options) { + // Only add headers if running in OpenFrame mode + if (!mesh.openFrameMode) return options; + + if (!options.headers) options.headers = {}; + + // Add x-machine-id header + var machineId = getOpenFrameMachineId(); + if (machineId) { + options.headers['x-machine-id'] = machineId; + } + + // Add Authorization header with JWT token + var token = mesh.authToken(); + if (token) { + options.headers['Authorization'] = 'Bearer ' + token; + } + + return options; +} var amt = null; var processManager = require('process-manager'); var wifiScannerLib = null; @@ -1169,6 +1206,7 @@ function handleServerCommand(data) { //sendConsoleText(JSON.stringify(woptions)); //sendConsoleText('TUNNEL: ' + JSON.stringify(data, null, 2)); + addOpenFrameHeaders(woptions); // Add X-MACHINE-ID and Authorization headers var tunnel = http.request(woptions); tunnel.upgrade = onTunnelUpgrade; tunnel.on('error', tunnel_onError); @@ -1812,6 +1850,7 @@ function downloadFile(downloadoptions) { if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') } } //options.checkServerIdentity.servertlshash = downloadoptions.serverhash; + addOpenFrameHeaders(options); // Add X-MACHINE-ID header trustedDownloads[downloadoptions.name] = downloadoptions; trustedDownloads[downloadoptions.name].dl = require('https').get(options); trustedDownloads[downloadoptions.name].dl.on('error', function (e) { downloadoptions.func(downloadoptions, false); delete trustedDownloads[downloadoptions.name]; }); @@ -1864,6 +1903,7 @@ function serverFetchFile() { agentFileHttpOptions.checkServerIdentity.servertlshash = data.servertlshash; if (agentFileHttpOptions == null) return; + addOpenFrameHeaders(agentFileHttpOptions); // Add X-MACHINE-ID header var agentFileHttpRequest = http.request(agentFileHttpOptions, function (response) { response.xparent = this; @@ -5096,6 +5136,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) { if (options == null) { response = 'Invalid url.'; } else { + addOpenFrameHeaders(options); // Add X-MACHINE-ID header try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (ex) { response = 'Invalid HTTP GET request'; } consoleHttpRequest.sessionid = sessionid; if (consoleHttpRequest != null) { @@ -5124,6 +5165,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) { try { var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL options.rejectUnauthorized = 0; + addOpenFrameHeaders(options); // Add X-MACHINE-ID header httprequest = http.request(options); } catch (ex) { response = 'Invalid HTTP websocket request'; } if (httprequest != null) { @@ -5773,6 +5815,7 @@ function agentUpdate_Start(updateurl, updateoptions) { } } options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null); + addOpenFrameHeaders(options); // Add X-MACHINE-ID header agentUpdate_Start._selfupdate = require('https').get(options); agentUpdate_Start._selfupdate.on('error', function (e) { sendConsoleText('Self Update failed, because there was a problem trying to download the update from ' + updateurl, sessionid); diff --git a/makefile b/makefile index ff7622676..69fc51dc1 100644 --- a/makefile +++ b/makefile @@ -186,7 +186,7 @@ SOURCES += $(ADDITIONALSOURCES) ADDITIONALOBJECTS ?= # Mesh Agent core -SOURCES += meshcore/agentcore.c meshconsole/main.c meshcore/meshinfo.c openframe/token_extractor.c +SOURCES += meshcore/agentcore.c meshconsole/main.c meshcore/meshinfo.c openframe/token_extractor.c openframe/machine_id_reader.c # Mesh Agent settings MESH_VER = 194 diff --git a/meshconsole/MeshConsole-2022.vcxproj b/meshconsole/MeshConsole-2022.vcxproj index 473add809..67a60debe 100644 --- a/meshconsole/MeshConsole-2022.vcxproj +++ b/meshconsole/MeshConsole-2022.vcxproj @@ -82,6 +82,7 @@ + @@ -138,6 +139,7 @@ + diff --git a/meshcore/agentcore.c b/meshcore/agentcore.c index c3994d12f..84cef21e4 100644 --- a/meshcore/agentcore.c +++ b/meshcore/agentcore.c @@ -45,6 +45,7 @@ limitations under the License. #include "microscript/ILibDuktape_ScriptContainer.h" #include "../microstack/ILibIPAddressMonitor.h" #include "../openframe/token_extractor.h" +#include "../openframe/machine_id_reader.h" #ifdef _POSIX #include @@ -2344,6 +2345,7 @@ void ILibDuktape_MeshAgent_PUSH(duk_context *ctx, void *chain) ILibDuktape_CreateInstanceMethod(ctx, "DataPing", ILibDuktape_MeshAgent_DataPing, DUK_VARARGS); ILibDuktape_CreateReadonlyProperty_int(ctx, "ARCHID", MESH_AGENTID); ILibDuktape_CreateReadonlyProperty_int(ctx, "ConsoleTextMaxRate", agent->consoleText_maxRate); + ILibDuktape_CreateReadonlyProperty_int(ctx, "openFrameMode", agent->openFrameMode ? 1 : 0); #ifdef _LINKVM ILibDuktape_CreateReadonlyProperty_int(ctx, "hasKVM", 1); ILibDuktape_EventEmitter_CreateEventEx(emitter, "kvmConnected"); @@ -4614,19 +4616,30 @@ void MeshServer_ConnectEx(MeshAgentHostContainer *agent) strcat(combined, SOURCE_COMMIT_DATE); ILibAddHeaderLine(req, "User-Agent", 10, combined, (int)strnlen_s(combined, 50)); - // Add custom JWT for openframe mode - if (agent->openFrameMode && agent->openFrameSecret != NULL) + // Add custom headers for openframe mode + if (agent->openFrameMode) { - char* extracted_token = extract_token(agent->openFrameSecret, agent->openFrameTokenPath); - if (extracted_token != NULL) { - int authLen = 7 + strlen(extracted_token) + 1; // "Bearer " + token + null terminator - char *openframeAuthorization = (char*)malloc(authLen); - if (openframeAuthorization != NULL) { - sprintf(openframeAuthorization, "Bearer %s", extracted_token); - ILibAddHeaderLine(req, "Authorization", 13, openframeAuthorization, (int)strlen(openframeAuthorization)); - free(openframeAuthorization); + // Add x-machine-id header + char* machineId = read_machine_id(); + if (machineId != NULL) { + ILibAddHeaderLine(req, "x-machine-id", 12, machineId, (int)strlen(machineId)); + free(machineId); + } + + // Add Authorization header with JWT + if (agent->openFrameSecret != NULL) + { + char* extracted_token = extract_token(agent->openFrameSecret, agent->openFrameTokenPath); + if (extracted_token != NULL) { + int authLen = 7 + strlen(extracted_token) + 1; // "Bearer " + token + null terminator + char *openframeAuthorization = (char*)malloc(authLen); + if (openframeAuthorization != NULL) { + sprintf(openframeAuthorization, "Bearer %s", extracted_token); + ILibAddHeaderLine(req, "Authorization", 13, openframeAuthorization, (int)strlen(openframeAuthorization)); + free(openframeAuthorization); + } + free(extracted_token); } - free(extracted_token); } } diff --git a/meshservice/MeshService-2022.vcxproj b/meshservice/MeshService-2022.vcxproj index 3f5cc1fce..22d05a59d 100644 --- a/meshservice/MeshService-2022.vcxproj +++ b/meshservice/MeshService-2022.vcxproj @@ -822,6 +822,7 @@ powershell -ExecutionPolicy Unrestricted $(ProjectDir)prebuild.ps1 $(ProjectDir) + @@ -879,6 +880,7 @@ powershell -ExecutionPolicy Unrestricted $(ProjectDir)prebuild.ps1 $(ProjectDir) + diff --git a/microscript/ILibDuktape_Commit.h b/microscript/ILibDuktape_Commit.h index ba83c1bef..9b2e7580f 100644 --- a/microscript/ILibDuktape_Commit.h +++ b/microscript/ILibDuktape_Commit.h @@ -1,3 +1,3 @@ // This file is auto-generated, any edits may be overwritten -#define SOURCE_COMMIT_DATE "2026-Mar-3 13:49:57+0000" -#define SOURCE_COMMIT_HASH "26d32854a436dc65e45ee3576bf602c898444cc1" +#define SOURCE_COMMIT_DATE "2026-Mar-4 12:31:15+0000" +#define SOURCE_COMMIT_HASH "1242c456119125053a41282337d3f7d159ac4784" diff --git a/modules/RecoveryCore.js b/modules/RecoveryCore.js index 762fa91f5..aedef9dd8 100644 --- a/modules/RecoveryCore.js +++ b/modules/RecoveryCore.js @@ -6,6 +6,43 @@ var nextTunnelIndex = 1; var tunnels = {}; var fs = require('fs'); +// OpenFrame: Read machine ID from shared location +var openframeMachineId = null; +function getOpenFrameMachineId() { + if (openframeMachineId != null) return openframeMachineId; + try { + var machineIdPath = (process.platform == 'win32') + ? (process.env['ProgramData'] + '\\OpenFrame\\machine_id') + : ((process.platform == 'darwin') + ? '/Library/Application Support/OpenFrame/machine_id' + : '/var/lib/openframe/machine_id'); + openframeMachineId = fs.readFileSync(machineIdPath).toString().trim(); + } catch (ex) { openframeMachineId = null; } + return openframeMachineId; +} + +// OpenFrame: Add x-machine-id and Authorization headers to request options (only in openFrameMode) +function addOpenFrameHeaders(options) { + var mesh = require('MeshAgent'); + if (!mesh.openFrameMode) return options; + + if (!options.headers) options.headers = {}; + + // Add x-machine-id header + var machineId = getOpenFrameMachineId(); + if (machineId) { + options.headers['x-machine-id'] = machineId; + } + + // Add Authorization header with JWT token + var token = mesh.authToken(); + if (token) { + options.headers['Authorization'] = 'Bearer ' + token; + } + + return options; +} + //attachDebugger({ webport: 9994, wait: 1 }).then(function (p) { console.log('Debug on port: ' + p); }); function sendConsoleText(msg) @@ -155,6 +192,7 @@ require('MeshAgent').AddCommandHandler(function (data) var woptions = http.parseUri(xurl); woptions.rejectUnauthorized = 0; //sendConsoleText(JSON.stringify(woptions)); + addOpenFrameHeaders(woptions); // Add X-MACHINE-ID header var tunnel = http.request(woptions); tunnel.on('upgrade', function (response, s, head) { diff --git a/openframe/machine_id_reader.c b/openframe/machine_id_reader.c new file mode 100644 index 000000000..c0e5f562f --- /dev/null +++ b/openframe/machine_id_reader.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include "machine_id_reader.h" + +#define MAX_MACHINE_ID_LEN 128 + +#ifdef _WIN32 +#include + +char* read_machine_id() { + // Read from C:\ProgramData\OpenFrame\machine_id + char path[MAX_PATH]; + char* programData = getenv("ProgramData"); + if (!programData) { + return NULL; + } + + snprintf(path, sizeof(path), "%s\\OpenFrame\\machine_id", programData); + + FILE* file = fopen(path, "r"); + if (!file) { + return NULL; + } + + char* machineId = malloc(MAX_MACHINE_ID_LEN); + if (!machineId) { + fclose(file); + return NULL; + } + + if (fgets(machineId, MAX_MACHINE_ID_LEN, file) == NULL) { + free(machineId); + fclose(file); + return NULL; + } + + fclose(file); + + // Trim newline + size_t len = strlen(machineId); + while (len > 0 && (machineId[len-1] == '\n' || machineId[len-1] == '\r')) { + machineId[--len] = '\0'; + } + + if (len == 0) { + free(machineId); + return NULL; + } + + return machineId; +} + +#else +// macOS / Linux + +char* read_machine_id() { +#ifdef __APPLE__ + const char* path = "/Library/Application Support/OpenFrame/machine_id"; +#else + const char* path = "/var/lib/openframe/machine_id"; +#endif + + FILE* file = fopen(path, "r"); + if (!file) { + return NULL; + } + + char* machineId = malloc(MAX_MACHINE_ID_LEN); + if (!machineId) { + fclose(file); + return NULL; + } + + if (fgets(machineId, MAX_MACHINE_ID_LEN, file) == NULL) { + free(machineId); + fclose(file); + return NULL; + } + + fclose(file); + + // Trim newline + size_t len = strlen(machineId); + while (len > 0 && (machineId[len-1] == '\n' || machineId[len-1] == '\r')) { + machineId[--len] = '\0'; + } + + if (len == 0) { + free(machineId); + return NULL; + } + + return machineId; +} + +#endif diff --git a/openframe/machine_id_reader.h b/openframe/machine_id_reader.h new file mode 100644 index 000000000..8b4162dbc --- /dev/null +++ b/openframe/machine_id_reader.h @@ -0,0 +1,22 @@ +#ifndef MACHINE_ID_READER_H +#define MACHINE_ID_READER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Read machine ID from shared OpenFrame location + * - macOS: /Library/Application Support/OpenFrame/machine_id + * - Windows: C:\ProgramData\OpenFrame\machine_id + * - Linux: /var/lib/openframe/machine_id + * + * @return Machine ID string (caller must free) or NULL if not found + */ +char* read_machine_id(void); + +#ifdef __cplusplus +} +#endif + +#endif // MACHINE_ID_READER_H