/* Copyright 2006 - 2017 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. */ #ifdef WIN32 #include #include #include #include #endif #include "agentcore.h" #include "signcheck.h" #include "meshdefines.h" #include "meshinfo.h" #include "microscript/ILibDuktape_Polyfills.h" #include "microscript/ILibDuktape_Helpers.h" #include "microscript/ILibDuktape_SHA256.h" #include "microscript/ILibDuktape_EncryptionStream.h" #include "microscript/ILibDuktape_DuplexStream.h" #include "microscript/ILibDuktape_EventEmitter.h" #include "microscript/ILibDuktape_net.h" #include "microscript/ILibDuktape_Dgram.h" #include "microstack/ILibParsers.h" #include "microstack/ILibAsyncUDPSocket.h" #include "microstack/ILibMulticastSocket.h" #include "microscript/ILibDuktape_ScriptContainer.h" #include "../microstack/ILibIPAddressMonitor.h" #ifndef _NOHECI #include "microlms/lms/ILibLMS.h" #endif #ifdef _POSIX #include #endif #ifdef _LINKVM #ifdef WIN32 #include "KVM/Windows/kvm.h" #endif #ifdef _POSIX #include "KVM/Linux/linux_kvm.h" #endif #endif #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(_MINCORE) #define _CRTDBG_MAP_ALLOC #include #endif #define HEX_IDENTIFIER (unsigned short)12408 #define EXE_IDENTIFIER (unsigned int)778401893 #define MSH_IDENTIFIER (unsigned int)778924904 #define SCRIPT_ENGINE_PIPE_BUFFER_SIZE 65535 #define MESH_AGENT_PORT 16990 //!< Default Mesh Agent Port #define MESH_MCASTv4_GROUP "239.255.255.235" #define MESH_MCASTv6_GROUP "FF02:0:0:0:0:0:0:FE" #define MESH_SCRIPTCONTAINER_ID "\xFF_ScriptContainer_ID" #define MESH_AGENT_SINGLETON "\xFF_MeshAgentObject_Singleton" #define SEQ_TABLE_KEY "\xFF_seqTable" #define CONTAINER_PTR "\xFF_ptr" #define MESH_AGENT_PTR "\xFFMeshAgentPtr" #define MESH_AGENT_DUKPTRS "\xFFptrs" #define CTX_PTR "\xFF_Heap" #define CONTEXT_GUID_PTR "_CONTEXT_GUID_PTR" #define REMOTE_DESKTOP_STREAM "\xFF_RemoteDesktopStream" #define REMOTE_DESKTOP_ptrs "\xFF_RemoteDesktopPTRS" #define DEFAULT_IDLE_TIMEOUT 120 char* MeshAgentHost_BatteryInfo_STRINGS[] = { "UNKNOWN", "HIGH_CHARGE", "LOW_CHARGE", "NO_BATTERY", "CRITICAL_CHARGE", "", "", "", "CHARGING" }; JS_ENGINE_CONTEXT MeshAgent_JavaCore_ContextGuid = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; typedef struct RemoteDesktop_Ptrs { duk_context *ctx; void *object; void *MeshAgentObject; #ifdef _POSIX void *kvmPipe; #endif ILibDuktape_DuplexStream *stream; }RemoteDesktop_Ptrs; typedef struct ScriptContainerSettings { SCRIPT_ENGINE_SECURITY_FLAGS permissions; unsigned int executionTimeout; int usingMasterDb; ILibDuktape_NativeUncaughtExceptionHandler nUncaughtExceptionHandler; void *nUncaughtExceptionUser; char ContextGuid[sizeof(JS_ENGINE_CONTEXT) + 1]; }ScriptContainerSettings; #define ScriptContainerSettingsKey "\xFF_ScriptContainerSettings" ScriptContainerSettings* ScriptEngine_GetSettings(duk_context *ctx) { ScriptContainerSettings *retVal = NULL; duk_push_heap_stash(ctx); // [stash] if (duk_has_prop_string(ctx, -1, ScriptContainerSettingsKey)) { duk_get_prop_string(ctx, -1, ScriptContainerSettingsKey); // [stash][buffer] retVal = (ScriptContainerSettings*)Duktape_GetBuffer(ctx, -1, NULL); duk_pop_2(ctx); // ... return retVal; } else { duk_pop(ctx); // ... return NULL; } } typedef struct SCRIPT_ENGINE_ISOLATION { unsigned int reserved; MeshAgentHostContainer *agent; ILibProcessPipe_Process process; duk_context *ctx; ILibDuktape_EventEmitter *emitter; void *ScriptContainer; void *OnExit; void *OnError; unsigned int seq; char *containerID; duk_size_t containerIDLen; #ifdef WIN32 char* buffer; int bufferLen; #endif }SCRIPT_ENGINE_ISOLATION; typedef enum SCRIPT_ENGINE_COMMAND { SCRIPT_ENGINE_COMMAND_INIT = 0x01, SCRIPT_ENGINE_COMMAND_EXEC_PATH = 0x02, SCRIPT_ENGINE_COMMAND_EXEC_STR = 0x03, SCRIPT_ENGINE_COMMAND_EXEC_OK = 0x04, SCRIPT_ENGINE_COMMAND_EXEC_ERR = 0xFE, SCRIPT_ENGINE_COMMAND_QUERY = 0x20, SCRIPT_ENGINE_COMMAND_SET = 0x21, SCRIPT_ENGINE_COMMAND_DB_GET = 0x10, SCRIPT_ENGINE_COMMAND_DB_PUT = 0x11, SCRIPT_ENGINE_COMMAND_LOG = 0xFF }SCRIPT_ENGINE_COMMAND; #pragma pack(push, 1) typedef struct SCRIPT_ENGINE_COMMAND_HEADER { unsigned short length; unsigned short cmdType; char data[]; }SCRIPT_ENGINE_COMMAND_HEADER; typedef struct SCRIPT_ENGINE_COMMAND_INIT_DATA { JS_ENGINE_CONTEXT contextGuid; unsigned int securityFlags; unsigned int executionTimeout; }SCRIPT_ENGINE_COMMAND_INIT_DATA; typedef struct SCRIPT_ENGINE_COMMAND_LOG_DATA { ILibRemoteLogging_Modules module; ILibRemoteLogging_Flags flags; char msg[]; }SCRIPT_ENGINE_COMMAND_LOG_DATA; typedef struct SCRIPT_ENGINE_COMMAND_EXEC_ERR_DATA { JS_ENGINE_CONTEXT contextGuid; int seq; char errorMessage[]; }SCRIPT_ENGINE_COMMAND_EXEC_ERR_DATA; typedef struct SCRIPT_ENGINE_COMMAND_EXEC_OK_DATA { JS_ENGINE_CONTEXT contextGuid; int seq; char retVal_JSON[]; }SCRIPT_ENGINE_COMMAND_EXEC_OK_DATA; typedef struct SCRIPT_ENGINE_COMMAND_EXEC_STR_DATA { JS_ENGINE_CONTEXT contextGuid; int seq; char script[]; }SCRIPT_ENGINE_COMMAND_EXEC_STR_DATA; typedef struct SCRIPT_ENGINE_COMMAND_DB_GET_DATA { JS_ENGINE_CONTEXT contextGuid; char key[]; }SCRIPT_ENGINE_COMMAND_DB_GET_DATA; typedef struct SCRIPT_ENGINE_COMMAND_DB_PUT_DATA { JS_ENGINE_CONTEXT contextGuid; int valueOffset; char data[]; }SCRIPT_ENGINE_COMMAND_DB_PUT_DATA; #pragma pack(pop) const char* AgentID_String[24] = { "UNKNOWN", "Windows Console/x86", "Windows Console/x64", "Windows Service/x86", "Windows Service/x64", "Linux/x86", "Linux/x64", "MIPS", "XEN/x86", "Android/ARM", "Linux/ARM", "MAC OS/x86", "Android/x86", "PogoPlug/ARM", "Android (Google Play)", "Linux(Poky)/x86", "MAC OS/x64", "ChromeOS", "Linux(Poky)/x64", "Linux (NoKVM)/x86", "Linux (NoKVM)/x64", "Windows Console (MIN)", "Windows Service (MIN)", "NodeJS" }; typedef enum AGENT_RECORD_TYPE { AGENT_RECORD_TYPE_INIT = 0x00000001, AGENT_RECORD_TYPE_SHARED_DB = 0x00000003, AGENT_RECORD_TYPE_JAVASCRIPT_PAYLOAD = 0x00000004, AGENT_RECORD_TYPE_DATA_SET = 0x00000006, AGENT_RECORD_TYPE_DATA_QUERY = 0x00000008 }AGENT_RECORD_TYPE; typedef struct AGENT_RECORD_HEADER { unsigned int RecordLength; AGENT_RECORD_TYPE RecordType; JS_ENGINE_CONTEXT Context; char Payload[]; }AGENT_RECORD_HEADER; // // Piped Data Layout: // // 4 Bytes: Record Length // 4 Bytes: Record Type // 16 Bytes: Context GUID // Variable: Payload // Payloads ==> // Init Record Payload // 4 Bytes: Security Flags // 4 Bytes: Execution Timeout // Javascript Payload // JS Payload // Shared DB Payload // DB Path // Environment Payload // 2 Bytes: VarLen // 2 Bytes: VarValueLen // Variable: VarName // Variable: VarValue // Data Query // 2 Bytes: QueryLen // Variable: Query void MeshServer_Connect(MeshAgentHostContainer *agent); void MeshServer_ProcessCommand(ILibWebClient_StateObject wcdo, MeshAgentHostContainer *agent, char *buffer, int bufferLen); char ContainerContextGUID[sizeof(JS_ENGINE_CONTEXT) + 1]; void MeshServer_ConnectEx(MeshAgentHostContainer *agent); #ifdef _REMOTELOGGING void MeshAgent_Slave_LogForward(ILibRemoteLogging sender, ILibRemoteLogging_Modules module, ILibRemoteLogging_Flags flags, char *buffer, int bufferLen) { SCRIPT_ENGINE_COMMAND_HEADER* header = (SCRIPT_ENGINE_COMMAND_HEADER*)buffer; buffer[bufferLen] = 0; header->cmdType = SCRIPT_ENGINE_COMMAND_LOG; header->length = bufferLen+1; ((SCRIPT_ENGINE_COMMAND_LOG_DATA*)header->data)->module = module; ((SCRIPT_ENGINE_COMMAND_LOG_DATA*)header->data)->flags = flags; #ifdef WIN32 WriteFile(GetStdHandle(STD_ERROR_HANDLE), (void*)buffer, ((SCRIPT_ENGINE_COMMAND_HEADER*)buffer)->length, &bufferLen, NULL); #else ignore_result(write(STDERR_FILENO, buffer, ((SCRIPT_ENGINE_COMMAND_HEADER*)buffer)->length)); #endif } #endif ILibDuktape_EventEmitter* MeshAgent_GetEventEmitter_FromCTX(duk_context *ctx) { ILibDuktape_EventEmitter* retVal = NULL; duk_push_heap_stash(ctx); // [stash] if (duk_has_prop_string(ctx, -1, MESH_AGENT_SINGLETON)) { duk_get_prop_string(ctx, -1, MESH_AGENT_SINGLETON); // [stash][MeshAgent] retVal = ILibDuktape_EventEmitter_GetEmitter(ctx, -1); duk_pop_2(ctx); // ... } else { duk_pop(ctx); // ... } return retVal; } void MeshAgent_Slave_SendCommand(MeshAgentHostContainer *agent, char *buffer, int bufferLen) { if (agent->slaveMode != 0) { #ifdef WIN32 int writeLen; WriteFile(GetStdHandle(STD_ERROR_HANDLE), (void*)buffer, bufferLen, &writeLen, NULL); #else ignore_result(write(STDERR_FILENO, buffer, bufferLen)); #endif } } // This is used in windows console mode to detect if Intel LMS is running #ifdef WIN32 #ifndef _WINSERVICE int GetServiceState(LPCSTR servicename) { int r = 0; SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); if (serviceControlManager) { SC_HANDLE service = OpenService(serviceControlManager, servicename, SERVICE_QUERY_STATUS | DELETE); if (service) { SERVICE_STATUS serviceStatusEx; if (QueryServiceStatus(service, &serviceStatusEx)) { r = serviceStatusEx.dwCurrentState; } CloseServiceHandle(service); } else { r = 100; } CloseServiceHandle(serviceControlManager); } return r; } #else extern int GetServiceState(LPCSTR servicename); #endif #endif /* ------------------------------ Begin Mesh Agent Multicast Support This is used only when "MeshServer=local" in .msh policy file --------------------------------*/ // Called when a UDP packet is received void UDPSocket_OnData(ILibAsyncUDPSocket_SocketModule socketModule, char* buffer, int bufferLength, struct sockaddr_in6 *remoteInterface, void *user, void *user2, int *PAUSE) { //int isLoopback; MeshAgentHostContainer *agentHost = (MeshAgentHostContainer*)user; UNREFERENCED_PARAMETER(socketModule); UNREFERENCED_PARAMETER(user); UNREFERENCED_PARAMETER(user2); UNREFERENCED_PARAMETER(PAUSE); // Perform basic checks before processing this packet if (remoteInterface->sin6_family != AF_INET && remoteInterface->sin6_family != AF_INET6) return; //isLoopback = ILibIsLoopback((struct sockaddr*)remoteInterface); // Check if this is a Mesh Server discovery packet and it is for our server // It will have this form: "MeshCentral2|f5a50091028fe2c122434cbcbd2709a7ec10369295e5a0e43db8853a413d89df|wss://~:443/agent.ashx" if ((bufferLength > 78) && (memcmp(buffer, "MeshCentral2|", 13) == 0) && ((ILibSimpleDataStore_Get(agentHost->masterDb, "ServerID", ILibScratchPad, sizeof(ILibScratchPad))) == 65) && (memcmp(ILibScratchPad, buffer + 13, 64) == 0)) { // We have a match, set the server URL correctly. if (agentHost->multicastServerUrl != NULL) { free(agentHost->multicastServerUrl); agentHost->multicastServerUrl = NULL; } if ((agentHost->multicastServerUrl = (char*)malloc(bufferLength - 78 + 128)) == NULL) { ILIBCRITICALEXIT(254); } buffer[bufferLength] = 0; ILibInet_ntop2((struct sockaddr*)remoteInterface, (char*)ILibScratchPad2, sizeof(ILibScratchPad)); sprintf_s(agentHost->multicastServerUrl, bufferLength - 78 + 128, buffer + 78, ILibScratchPad2); //printf("FoundServer: %s\r\n", agentHost->multicastServerUrl); if (agentHost->serverConnectionState == 0) { MeshServer_ConnectEx(agentHost); } } } /* // Called with a 5 second lag time when an interface changes void IPAddressMonitorUpdate(void *data) { UNREFERENCED_PARAMETER(data); // Setup the multicast timer //MSG("IPAddressMonitorUpdate.\r\n"); #ifdef WIN32 // In Windows, just reset the broadcast timer to 15 seconds ILibLifeTime_Remove(Mesh.Timer, (void*)2); ILibLifeTime_Add(Mesh.Timer, (void*)2, 15, &TimerTriggered, &TimerDestroyed); #else // In Linux, we need to check to see if the push block has changed ctrl_GetCurrentSignedNodeInfoBlock(NULL); if (Mesh.LastMulticastPushSerial != g_serial) { Mesh.LastMulticastPushSerial = g_serial; ILibLifeTime_Remove(Mesh.Timer, (void*)2); ILibLifeTime_Add(Mesh.Timer, (void*)2, 30, &TimerTriggered, &TimerDestroyed); } #endif // If the service is not connected, reduce the time to the next attempt to 5 seconds if (g_ServiceConnected == 0) { ILibLifeTime_Remove(Mesh.Timer, (void*)4); ILibLifeTime_Add(Mesh.Timer, (void*)4, 5, &TimerTriggered, &TimerDestroyed); } } // Method gets periodically executed on the microstack thread to update the list of known IP addresses. #ifdef WINSOCK2 void CALLBACK IPAddressMonitor ( IN DWORD dwError, IN DWORD cbTransferred, IN LPWSAOVERLAPPED lpOverlapped, IN DWORD dwFlags ) #else void IPAddressMonitor(void *data) #endif { #ifdef WINSOCK2 UNREFERENCED_PARAMETER(dwError); UNREFERENCED_PARAMETER(cbTransferred); UNREFERENCED_PARAMETER(lpOverlapped); UNREFERENCED_PARAMETER(dwFlags); #endif // We are in the process of cleaning up, lets exit now if (Mesh.MulticastSocket == NULL) return; #ifdef WINSOCK2 // Call the interface update with a lag timer. The short lag allows interfaces to stabilize. ILibLifeTime_Remove(Mesh.Timer, ILibScratchPad); // Here we use "ILibScratchPad" as a dummy global identifier, memory not actualy used ILibLifeTime_Add(Mesh.Timer, ILibScratchPad, 6, &IPAddressMonitorUpdate, NULL); // Here we use "ILibScratchPad" as a dummy global identifier, memory not actualy used WSAIoctl(NetworkMonitorSocket, SIO_ADDRESS_LIST_CHANGE, NULL, 0, NULL, 0, &UpnpMonitorSocketReserved, &UpnpMonitorSocketStateObject, &IPAddressMonitor); #else // Call the interface update directly. TODO: This is very innefficient, we need to fix this. IPAddressMonitorUpdate(NULL); ILibLifeTime_Add(Mesh.Timer, NULL, 20, &IPAddressMonitor, NULL); #endif } */ /* ------------------------------ Begin Mesh Agent Duktape Abstraction --------------------------------*/ MeshAgentDuktapePtrs* ILibDuktape_MeshAgent_GetMeshAgentPtrs(duk_context *ctx) { MeshAgentDuktapePtrs *ptrs = NULL; MeshAgentHostContainer *agent = NULL; duk_push_this(ctx); // [MeshAgent] if (duk_has_prop_string(ctx, -1, MESH_AGENT_DUKPTRS)) { // We already created a binding earlier duk_get_prop_string(ctx, -1, MESH_AGENT_DUKPTRS); // [MeshAgent][ptrs] ptrs = (MeshAgentDuktapePtrs*)Duktape_GetBuffer(ctx, -1, NULL); duk_pop(ctx); // [MeshAgent] } else { // Create a new binding duk_push_fixed_buffer(ctx, sizeof(MeshAgentDuktapePtrs)); // [MeshAgent][buffer] ptrs = (MeshAgentDuktapePtrs*)Duktape_GetBuffer(ctx, -1, NULL); duk_put_prop_string(ctx, -2, MESH_AGENT_DUKPTRS); // [MeshAgent] memset(ptrs, 0, sizeof(MeshAgentDuktapePtrs)); ptrs->ctx = ctx; ptrs->MeshAgentObject = duk_get_heapptr(ctx, -1); duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); // [MeshAgent][Host] agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); duk_pop(ctx); // [MeshAgent] ptrs->Next = agent->DuktapeMeshBindings; agent->DuktapeMeshBindings = ptrs; } duk_pop(ctx); // ... return ptrs; } duk_ret_t ILibDuktape_MeshAgent_AddCommandHandler(duk_context *ctx) { MeshAgentDuktapePtrs *ptrs; void *OnCommand = duk_require_heapptr(ctx, 0); ptrs = ILibDuktape_MeshAgent_GetMeshAgentPtrs(ctx); ptrs->OnCommand = OnCommand; return 0; } duk_ret_t ILibDuktape_MeshAgent_AddConnectHandler(duk_context *ctx) { MeshAgentDuktapePtrs *ptrs; void *OnConnect = duk_require_heapptr(ctx, 0); ptrs = ILibDuktape_MeshAgent_GetMeshAgentPtrs(ctx); ptrs->OnConnect = OnConnect; return 0; } duk_ret_t ILibDuktape_MeshAgent_Finalizer(duk_context *ctx) { MeshAgentDuktapePtrs *ptrs = NULL, *binding = NULL; MeshAgentHostContainer *agent = NULL; duk_dup(ctx, 0); // [MeshAgent] duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); // [MeshAgent][MeshAgentPtr] agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); if (duk_has_prop_string(ctx, -2, MESH_AGENT_DUKPTRS)) { duk_get_prop_string(ctx, -2, MESH_AGENT_DUKPTRS); // [MeshAgent][MeshAgentPtr][ptrs] ptrs = (MeshAgentDuktapePtrs*)Duktape_GetBuffer(ctx, -1, NULL); if (agent->DuktapeMeshBindings == ptrs) { agent->DuktapeMeshBindings = ptrs->Next; } else { binding = agent->DuktapeMeshBindings; while (binding->Next != NULL) { if (binding->Next == ptrs) { binding->Next = ptrs->Next; break; } binding = binding->Next; } } } return 0; } duk_ret_t ILibDuktape_MeshAgent_GenerateCertificate(duk_context *ctx) { #ifdef MICROSTACK_NOTLS return ILibDuktape_Error(ctx, "MeshAgent.generateCertificate(): Error, OpenSSL Support Disabled"); #else char *passphrase = (char*)duk_require_string(ctx, 0); int len; struct util_cert cert; char *data; #ifdef _REMOTELOGGING MeshAgentHostContainer *agent; duk_push_this(ctx); // [agent] duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); // [agent][ptr] agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); // Generate a new TLS certificate ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Generating JS TLS Certificate"); #endif len = util_mkCert(NULL, &(cert), 2048, 10000, "localhost", CERTIFICATE_TLS_CLIENT, NULL); len = util_to_p12(cert, passphrase, &data); duk_push_fixed_buffer(ctx, len); memcpy_s((void*)Duktape_GetBuffer(ctx, -1, NULL), len, data, len); util_free(data); util_freecert(&cert); return 1; #endif } // Javascript SendCommand(obj), send some data to the MeshCentral server // This method can handle buffers, string or objects as input. duk_ret_t ILibDuktape_MeshAgent_SendCommand(duk_context *ctx) { MeshAgentHostContainer *agent; char *buffer; duk_size_t bufferLen; // Get the pointer to the agent object duk_push_this(ctx); // [MeshAgent] duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); // [MeshAgent][Ptr] agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); if (agent->controlChannel == NULL) { duk_push_int(ctx, (int)ILibTransport_DoneState_ERROR); // Agent not connected } #ifndef MICROSTACK_NOTLS else if (agent->serverAuthState != 3) { // Server connection is not authenticated, SendCommand is not allowed. duk_push_int(ctx, (int)ILibTransport_DoneState_ERROR); } #endif else if (duk_is_buffer(ctx, 0) || duk_is_buffer_data(ctx, 0)) { // We are trying to send a buffer buffer = Duktape_GetBuffer(ctx, 0, &bufferLen); if (bufferLen > 0) { duk_push_int(ctx, (int)ILibWebClient_WebSocket_Send(agent->controlChannel, buffer[0] == '{' ? ILibWebClient_WebSocket_DataType_TEXT : ILibWebClient_WebSocket_DataType_BINARY, buffer, (int)bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete)); } else { duk_push_int(ctx, (int)ILibTransport_DoneState_ERROR); } } else if (duk_is_string(ctx, 0)) { // We are trying to send a string buffer = (char*)duk_get_lstring(ctx, 0, &bufferLen); duk_push_int(ctx, (int)ILibWebClient_WebSocket_Send(agent->controlChannel, ILibWebClient_WebSocket_DataType_TEXT, buffer, (int)bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete)); } else { // We are trying to send an object, perform JSON serialization first duk_dup(ctx, 0); // [object] duk_json_encode(ctx, -1); // [json] buffer = (char*)duk_get_lstring(ctx, -1, &bufferLen); duk_push_int(ctx, (int)ILibWebClient_WebSocket_Send(agent->controlChannel, ILibWebClient_WebSocket_DataType_TEXT, buffer, (int)bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete)); } return 1; } void ILibDuktape_MeshAgent_Ready(ILibDuktape_EventEmitter *sender, char *eventName, void *hookedCallback) { MeshAgentHostContainer *agent; duk_push_heapptr(sender->ctx, sender->object); // [agent] duk_get_prop_string(sender->ctx, -1, MESH_AGENT_PTR); // [MeshAgent][ptr] agent = (MeshAgentHostContainer*)duk_get_pointer(sender->ctx, -1); duk_pop(sender->ctx); // [MeshAgent] if (agent->controlChannel != NULL) { // MeshAgent is already 'Ready' duk_push_heapptr(sender->ctx, hookedCallback); // [MeshAgent][callback] duk_swap_top(sender->ctx, -2); // [callback][this] if (duk_pcall_method(sender->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(sender->ctx, "Error: MeshAgent_OnReady"); } } duk_pop(sender->ctx); // ... } #ifdef _LINKVM ILibTransport_DoneState ILibDuktape_MeshAgent_RemoteDesktop_KVM_WriteSink(char *buffer, int bufferLen, void *reserved) { RemoteDesktop_Ptrs *ptrs = (RemoteDesktop_Ptrs*)reserved; if (ptrs->stream != NULL) { if (ILibDuktape_DuplexStream_WriteData(ptrs->stream, buffer, bufferLen) != ILibTransport_DoneState_ERROR) { return ILibTransport_DoneState_COMPLETE; // Always returning complete, because we'll let the stream object handle flow control } else { return ILibTransport_DoneState_ERROR; } } return ILibTransport_DoneState_ERROR; } ILibTransport_DoneState ILibDuktape_MeshAgent_RemoteDesktop_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) { #ifdef _LINKVM #ifdef WIN32 kvm_relay_feeddata(buffer, bufferLen, ILibDuktape_MeshAgent_RemoteDesktop_KVM_WriteSink, user); #else kvm_relay_feeddata(buffer, bufferLen); #endif #endif return ILibTransport_DoneState_COMPLETE; } void ILibDuktape_MeshAgent_RemoteDesktop_EndSink(ILibDuktape_DuplexStream *stream, void *user) { // Peer disconnected the data channel RemoteDesktop_Ptrs *ptrs = (RemoteDesktop_Ptrs*)user; duk_push_heapptr(ptrs->ctx, ptrs->MeshAgentObject); // [MeshAgent] duk_del_prop_string(ptrs->ctx, -1, REMOTE_DESKTOP_STREAM); duk_pop(ptrs->ctx); // ... memset(ptrs, 0, sizeof(RemoteDesktop_Ptrs)); kvm_cleanup(); } void ILibDuktape_MeshAgent_RemoteDesktop_PauseSink(ILibDuktape_DuplexStream *sender, void *user) { #ifdef _POSIX ILibProcessPipe_Pipe_Pause(((RemoteDesktop_Ptrs*)user)->kvmPipe); #else kvm_pause(1); #endif } void ILibDuktape_MeshAgent_RemoteDesktop_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) { #ifdef _POSIX ILibProcessPipe_Pipe_Resume(((RemoteDesktop_Ptrs*)user)->kvmPipe); #else kvm_pause(0); #endif } duk_ret_t ILibDuktape_MeshAgent_RemoteDesktop_Finalizer(duk_context *ctx) { RemoteDesktop_Ptrs *ptrs; duk_get_prop_string(ctx, 0, REMOTE_DESKTOP_ptrs); ptrs = (RemoteDesktop_Ptrs*)Duktape_GetBuffer(ctx, -1, NULL); if (ptrs->ctx != NULL) { duk_push_heapptr(ptrs->ctx, ptrs->MeshAgentObject); // [MeshAgent] duk_del_prop_string(ptrs->ctx, -1, REMOTE_DESKTOP_STREAM); duk_pop(ptrs->ctx); // ... #ifdef _LINKVM kvm_cleanup(); #endif } return 0; } void ILibDuktape_MeshAgent_RemoteDesktop_PipeHook(ILibDuktape_readableStream *stream, void *user) { #ifdef _LINKVM #ifdef WIN32 ILibDuktape_DuplexStream *ds = (ILibDuktape_DuplexStream*)user; kvm_relay_reset(ILibDuktape_MeshAgent_RemoteDesktop_KVM_WriteSink, ds->user); #else kvm_relay_reset(); #endif #else UNREFERENCED_PARAMETER(stream); UNREFERENCED_PARAMETER(user); #endif } #endif duk_ret_t ILibDuktape_MeshAgent_getRemoteDesktop(duk_context *ctx) { #ifndef _LINKVM duk_push_null(ctx); return 1; #else RemoteDesktop_Ptrs *ptrs; MeshAgentHostContainer *agent; duk_push_this(ctx); // [MeshAgent] if (duk_has_prop_string(ctx, -1, REMOTE_DESKTOP_STREAM)) { duk_get_prop_string(ctx, -1, REMOTE_DESKTOP_STREAM); // [MeshAgent][RemoteDesktop] duk_get_prop_string(ctx, -1, REMOTE_DESKTOP_ptrs); ptrs = (RemoteDesktop_Ptrs*)Duktape_GetBuffer(ctx, -1, NULL); duk_pop(ctx); return 1; } duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); duk_pop(ctx); duk_push_object(ctx); // [MeshAgent][RemoteDesktop] duk_dup(ctx, -1); // [MeshAgent][RemoteDesktop][RemoteDesktop] duk_put_prop_string(ctx, -3, REMOTE_DESKTOP_STREAM); // [MeshAgent][RemoteDesktop] duk_push_fixed_buffer(ctx, sizeof(RemoteDesktop_Ptrs)); // [MeshAgent][RemoteDesktop][buffer] ptrs = (RemoteDesktop_Ptrs*)Duktape_GetBuffer(ctx, -1, NULL); duk_put_prop_string(ctx, -2, REMOTE_DESKTOP_ptrs); // [MeshAgent][RemoteDesktop] memset(ptrs, 0, sizeof(RemoteDesktop_Ptrs)); ptrs->MeshAgentObject = duk_get_heapptr(ctx, -2); ptrs->ctx = ctx; ptrs->object = duk_get_heapptr(ctx, -1); ptrs->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_MeshAgent_RemoteDesktop_WriteSink, ILibDuktape_MeshAgent_RemoteDesktop_EndSink, ILibDuktape_MeshAgent_RemoteDesktop_PauseSink, ILibDuktape_MeshAgent_RemoteDesktop_ResumeSink, ptrs); ILibDuktape_CreateFinalizer(ctx, ILibDuktape_MeshAgent_RemoteDesktop_Finalizer); ptrs->stream->readableStream->PipeHookHandler = ILibDuktape_MeshAgent_RemoteDesktop_PipeHook; // Setup Remote Desktop #ifdef WIN32 kvm_relay_setup(agent->exePath, agent->pipeManager, ILibDuktape_MeshAgent_RemoteDesktop_KVM_WriteSink, ptrs); #else ptrs->kvmPipe = kvm_relay_setup(agent->pipeManager, ILibDuktape_MeshAgent_RemoteDesktop_KVM_WriteSink, ptrs); #endif return 1; #endif } duk_ret_t ILibDuktape_MeshAgent_ConnectedServer(duk_context *ctx) { int len; MeshAgentHostContainer *agent; duk_push_this(ctx); duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); #ifndef MICROSTACK_NOTLS if (agent->controlChannel != NULL && agent->serverAuthState == 3) #else if (agent->controlChannel != NULL) #endif { len = ILibSimpleDataStore_Get(agent->masterDb, "MeshServer", ILibScratchPad2, sizeof(ILibScratchPad2)); if (len == 0) { duk_push_null(ctx); return 1; } parser_result *rs = ILibParseString(ILibScratchPad2, 0, len, ",", 1); parser_result_field *f = ILibParseString_GetResultIndex(rs, agent->serverIndex); f->datalength = ILibTrimString(&(f->data), f->datalength); f->data[f->datalength] = 0; duk_push_lstring(ctx, f->data, f->datalength); ILibDestructParserResults(rs); } else { duk_push_null(ctx); } return 1; } #ifndef _NOHECI duk_ret_t ILibDuktape_MeshAgent_MEInfo(duk_context *ctx) { char *data; int len = ILibLMS_GetMeInformation(&data, 0); if (len > 0) { duk_push_lstring(ctx, data, len); duk_json_decode(ctx, -1); free(data); } else { duk_push_null(ctx); } return 1; } #endif duk_ret_t ILibDuktape_MeshAgent_NetInfo(duk_context *ctx) { char *data; int len = MeshInfo_GetSystemInformation(&data); if (len > 0) { duk_push_lstring(ctx, data, len); duk_json_decode(ctx, -1); free(data); } else { duk_push_null(ctx); } return 1; } // Javascript ExecPowerState(int), executes power state command on the computer (Sleep, Hibernate...) duk_ret_t ILibDuktape_MeshAgent_ExecPowerState(duk_context *ctx) { int force = 0; int numArgs = (int)duk_get_top(ctx); duk_push_this(ctx); // [MeshAgent] if (numArgs == 2 && duk_is_number(ctx, 1)) { force = duk_get_int(ctx, 1); } if (duk_is_number(ctx, 0)) { duk_push_int(ctx, MeshInfo_PowerState((AgentPowerStateActions)duk_get_int(ctx, 0), force)); } else { duk_push_null(ctx); } return 1; } duk_ret_t ILibDuktape_MeshAgent_ServerUrl(duk_context *ctx) { duk_push_this(ctx); // [MeshAgent] duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); // [MeshAgent][ptr] MeshAgentHostContainer *agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); duk_push_string(ctx, agent->serveruri); // [MeshAgent][ptr][uri] return(1); } void ILibDuktape_MeshAgent_PUSH(duk_context *ctx, void *chain) { MeshAgentHostContainer *agent; ILibDuktape_EventEmitter *emitter; duk_push_heap_stash(ctx); // [stash] if (duk_has_prop_string(ctx, -1, MESH_AGENT_SINGLETON)) { duk_get_prop_string(ctx, -1, MESH_AGENT_SINGLETON); // [stash][MeshAgent] duk_swap_top(ctx, -2); // [MeshAgent][stash] duk_pop(ctx); // [MeshAgent] return; } duk_get_prop_string(ctx, -1, "MeshAgentPtr"); // [stash][agentPtr] agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); duk_pop_2(ctx); // ... duk_push_object(ctx); // [MeshAgent] duk_push_pointer(ctx, agent); // [MeshAgent][ptr] duk_put_prop_string(ctx, -2, MESH_AGENT_PTR); // [MeshAgent] duk_push_heap_stash(ctx); // [MeshAgent][stash] duk_dup(ctx, -2); // [MeshAgent][stash][MeshAgent] duk_put_prop_string(ctx, -2, MESH_AGENT_SINGLETON); // [MeshAgent][stash] duk_pop(ctx); // [MeshAgent] emitter = ILibDuktape_EventEmitter_Create(ctx); if (agent->slaveMode == 0) { #ifndef MICROSTACK_NOTLS duk_push_pointer(ctx, &agent->selftlscert); duk_put_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_Server); duk_push_pointer(ctx, &agent->selftlsclientcert); duk_put_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_Client); duk_push_pointer(ctx, &agent->selfcert); duk_put_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_NonLeaf); #endif duk_push_fixed_buffer(ctx, sizeof(MeshAgentDuktapePtrs)); // [MeshAgent][buffer] MeshAgentDuktapePtrs *ptrs = (MeshAgentDuktapePtrs*)Duktape_GetBuffer(ctx, -1, NULL); duk_put_prop_string(ctx, -2, MESH_AGENT_DUKPTRS); // [MeshAgent] memset(ptrs, 0, sizeof(MeshAgentDuktapePtrs)); ptrs->ctx = ctx; ptrs->MeshAgentObject = duk_get_heapptr(ctx, -1); ptrs->Next = agent->DuktapeMeshBindings; agent->DuktapeMeshBindings = ptrs; ILibDuktape_EventEmitter_CreateEvent(emitter, "Ready", &(ptrs->OnReady)); ILibDuktape_EventEmitter_CreateEvent(emitter, "Connected", &(ptrs->OnConnect)); ILibDuktape_EventEmitter_AddHook(emitter, "Ready", ILibDuktape_MeshAgent_Ready); ILibDuktape_CreateEventWithGetter(ctx, "ConnectedServer", ILibDuktape_MeshAgent_ConnectedServer); ILibDuktape_CreateEventWithGetter(ctx, "ServerUrl", ILibDuktape_MeshAgent_ServerUrl); ILibDuktape_CreateInstanceMethod(ctx, "getRemoteDesktopStream", ILibDuktape_MeshAgent_getRemoteDesktop, 0); ILibDuktape_CreateInstanceMethod(ctx, "AddCommandHandler", ILibDuktape_MeshAgent_AddCommandHandler, 1); ILibDuktape_CreateInstanceMethod(ctx, "AddConnectHandler", ILibDuktape_MeshAgent_AddConnectHandler, 1); ILibDuktape_CreateInstanceMethod(ctx, "SendCommand", ILibDuktape_MeshAgent_SendCommand, 1); ILibDuktape_CreateFinalizer(ctx, ILibDuktape_MeshAgent_Finalizer); ILibDuktape_CreateReadonlyProperty_int(ctx, "activeMicroLMS", (agent->microLMS != NULL ? 1 : 0)); #ifdef _LINKVM ILibDuktape_CreateReadonlyProperty_int(ctx, "hasKVM", 1); ILibDuktape_EventEmitter_CreateEventEx(emitter, "kvmConnected"); #else ILibDuktape_CreateReadonlyProperty_int(ctx, "hasKVM", 0); #endif #ifdef _NOHECI ILibDuktape_CreateReadonlyProperty_int(ctx, "hasHECI", 0); #else ILibDuktape_CreateReadonlyProperty_int(ctx, "hasHECI", 1); ILibDuktape_CreateEventWithGetter(ctx, "MEInfo", ILibDuktape_MeshAgent_MEInfo); ILibDuktape_EventEmitter_CreateEventEx(emitter, "lmsNotification"); #endif ILibDuktape_CreateEventWithGetter(ctx, "NetInfo", ILibDuktape_MeshAgent_NetInfo); ILibDuktape_CreateInstanceMethod(ctx, "ExecPowerState", ILibDuktape_MeshAgent_ExecPowerState, DUK_VARARGS); Duktape_CreateEnum(ctx, "ContainerPermissions", (char*[]) { "DEFAULT", "NO_AGENT", "NO_MARSHAL", "NO_PROCESS_SPAWNING", "NO_FILE_SYSTEM_ACCESS", "NO_NETWORK_ACCESS" }, (int[]) { 0x00, 0x10000000, 0x08000000, 0x04000000, 0x00000001, 0x00000002 }, 6); } ILibDuktape_CreateInstanceMethod(ctx, "GenerateCertificate", ILibDuktape_MeshAgent_GenerateCertificate, 1); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "Acquired MeshAgent"); } void ILibDuktape_MeshAgent_Init(duk_context* ctx, void *chain, MeshAgentHostContainer *agent) { duk_push_heap_stash(ctx); // [stash] duk_push_pointer(ctx, agent); // [stash][agentPtr] duk_put_prop_string(ctx, -2, "MeshAgentPtr"); // [stash] duk_pop(ctx); // ... ILibDuktape_ModSearch_AddHandler(ctx, "MeshAgent", ILibDuktape_MeshAgent_PUSH); } /* ------------------------------ End Mesh Agent Duktape Abstraction --------------------------------*/ // !!!WARNING!!!: The result of this method is stored in ILibScratchPad2 char* MeshAgent_MakeAbsolutePath(char *basePath, char *localPath) { size_t localPathLen = strnlen_s(localPath, sizeof(ILibScratchPad2)); size_t basePathLen = strnlen_s(basePath, sizeof(ILibScratchPad2)); int i, sz; if (localPath[0] == '.') { // Use Same executable Name, but different extension sz = (int)basePathLen; if (ntohl(((unsigned int*)(basePath + sz - 4))[0]) == EXE_IDENTIFIER) { // BasePath ends with .exe memcpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), basePath, sz - 4); memcpy_s(ILibScratchPad2 + sz - 4, sizeof(ILibScratchPad2) - sz - 4, localPath, localPathLen); sz = sz - 4 + (int)localPathLen; } else { // BasePath does not end with .exe memcpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), basePath, sz); memcpy_s(ILibScratchPad2 + sz, sizeof(ILibScratchPad2) - sz, localPath, localPathLen); sz += (int)localPathLen; } ILibScratchPad2[sz] = 0; } else { if ((i = ILibString_LastIndexOf(basePath, (int)basePathLen, "\\", 1)) < 0) { i = ILibString_LastIndexOf(basePath, (int)basePathLen, "/", 1); } i += 1; sz = i + (int)localPathLen; memcpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), basePath, i); memcpy_s(ILibScratchPad2 + i, sizeof(ILibScratchPad2) - i, localPath, localPathLen); ILibScratchPad2[sz] = 0; } return ILibScratchPad2; } #ifndef MICROSTACK_NOTLS int agent_GenerateCertificates(MeshAgentHostContainer *agent, char* certfile) { int len = -1; char* str; printf("Generating Certificates...\r\n"); ILibSimpleDataStore_Delete(agent->masterDb, "SelfNodeCert"); ILibSimpleDataStore_Delete(agent->masterDb, "SelfNodeTlsCert"); ILibSimpleDataStore_Delete(agent->masterDb, "SelfNodeTlsClientCert"); // Make sure the certs and database are clean util_freecert(&(agent->selfcert)); util_freecert(&(agent->selftlscert)); util_freecert(&(agent->selftlsclientcert)); if (certfile == NULL) { // Generate a new random node certificate ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Generating new Node Certificate"); do { if (util_mkCert(NULL, &(agent->selfcert), 2048, 10000, "MeshNodeCertificate", CERTIFICATE_ROOT, NULL) == 0) return -1; util_keyhash(agent->selfcert, agent->g_selfid); } while (((int*)agent->g_selfid)[0] == 0); // This removes any chance that the self_id starts with 32 bits of zeros. ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...g_selfid = %s", ILibRemoteLogging_ConvertToHex(agent->g_selfid, (int)sizeof(agent->g_selfid))); } else { // Load a node certificate from a PEM file ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Loading Node Cert from PEM file"); if (util_from_pem(certfile, &(agent->selfcert)) == -1) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "......*ERROR*"); return -1; } util_keyhash(agent->selfcert, agent->g_selfid); if (((int*)agent->g_selfid)[0] == 0) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "......g_selfid = *ERROR*"); return -1; // This removes any chance that the self_id starts with 32 bits of zeros. } ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...g_selfid = %s", ILibRemoteLogging_ConvertToHex(agent->g_selfid, (int)sizeof(agent->g_selfid))); } len = util_to_p12(agent->selfcert, "hidden", &str); ILibSimpleDataStore_PutEx(agent->masterDb, "SelfNodeCert", 12, str, len); util_free(str); // Generate a new TLS certificate ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Generating TLS Certificate"); len = util_mkCert(&(agent->selfcert), &(agent->selftlscert), 2048, 10000, "localhost", CERTIFICATE_TLS_SERVER, NULL); len = util_to_p12(agent->selftlscert, "hidden", &str); ILibSimpleDataStore_PutEx(agent->masterDb, "SelfNodeTlsCert", 15, str, len); util_free(str); // Generate a new TLS client certificate ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Generating TLS Client Certificate"); len = util_mkCert(&(agent->selfcert), &(agent->selftlsclientcert), 2048, 10000, "localhost", CERTIFICATE_TLS_CLIENT, NULL); len = util_to_p12(agent->selftlsclientcert, "hidden", &str); ILibSimpleDataStore_PutEx(agent->masterDb, "SelfNodeTlsClientCert", 21, str, len); util_free(str); MSG("Certificates ready.\r\n"); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...READY"); return 0; } int agent_LoadCertificates(MeshAgentHostContainer *agent) { char *buffer = ILibScratchPad2; int len; //printf("Loading Certificates...\r\n"); len = ILibSimpleDataStore_Get(agent->masterDb, "SelfNodeCert", buffer, sizeof(ILibScratchPad2)); if (len == 0 || util_from_p12(buffer, len, "hidden", &(agent->selfcert)) == 0) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Failed to load Node Certificate from Database"); return 1; } len = ILibSimpleDataStore_Get(agent->masterDb, "SelfNodeTlsCert", buffer, sizeof(ILibScratchPad2)); if (len == 0 || util_from_p12(buffer, len, "hidden", &(agent->selftlscert)) == 0) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Failed to load TLS Certificate from Database"); util_freecert(&(agent->selfcert)); return 1; } len = ILibSimpleDataStore_Get(agent->masterDb, "SelfNodeTlsClientCert", buffer, sizeof(ILibScratchPad2)); if (len == 0 || util_from_p12(buffer, len, "hidden", &(agent->selftlsclientcert)) == 0) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Failed to load TLS Client Certificate from Database"); util_freecert(&(agent->selfcert)); util_freecert(&(agent->selftlscert)); return 1; } util_keyhash(agent->selfcert, agent->g_selfid); return 0; } int agent_VerifyMeshCertificates(MeshAgentHostContainer *agent) { X509_STORE *store; X509_STORE_CTX *ctx; int i, j; // Check that the TLS certificate and TLS client certificate are correctly signed by our Mesh Agent certificate ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Verifying TLS Certificates are signed by Mesh Agent Certificate"); store = X509_STORE_new(); X509_STORE_add_cert(store, agent->selfcert.x509); // Check the TLS certificate ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(ctx, store, agent->selftlscert.x509, NULL); i = X509_verify_cert(ctx); X509_STORE_CTX_free(ctx); // Check the TLS client certificate ctx = X509_STORE_CTX_new(); X509_STORE_CTX_init(ctx, store, agent->selftlsclientcert.x509, NULL); j = X509_verify_cert(ctx); X509_STORE_CTX_free(ctx); X509_STORE_free(store); // If the certificate chain is not correct, re-create all the certificates. if (i != 1) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "......INVALID TLS Server Certificate"); } if (j != 1) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "......INVALID TLS Client Certificate"); } if (i != 1 || j != 1) { return 1; } // Bad certificates return 0; } #endif void WritePipeResponse(AGENT_RECORD_TYPE recordType, JS_ENGINE_CONTEXT engineContext, char *payload, int payloadLength) { #ifdef WIN32 int tmpLen; #endif AGENT_RECORD_HEADER *header = (AGENT_RECORD_HEADER*)ILibScratchPad2; header->RecordType = recordType; header->RecordLength = sizeof(AGENT_RECORD_HEADER) + payloadLength; memcpy_s(header->Context, sizeof(JS_ENGINE_CONTEXT), engineContext, sizeof(JS_ENGINE_CONTEXT)); if (payloadLength > 0) { memcpy_s(header->Payload, sizeof(ILibScratchPad2) - sizeof(AGENT_RECORD_HEADER), payload, payloadLength); } #ifdef WIN32 WriteFile(GetStdHandle(STD_ERROR_HANDLE), header, header->RecordLength, &tmpLen, NULL); #else ignore_result(write(STDERR_FILENO, header, header->RecordLength)); #endif } void ScriptEngine_Exit(MeshAgentHostContainer *agent, char *contextGUID, int exitCode) { duk_context *ctx = agent->meshCoreCtx; if (ctx != NULL) { void *procObj = ILibDuktape_GetProcessObject(ctx); duk_push_heapptr(ctx, procObj); // [process] duk_get_prop_string(ctx, -1, "exit"); // [process][exitFunc] duk_swap_top(ctx, -2); // [exitFunc][this/process] duk_push_int(ctx, exitCode); // [exitFunc][this/process][exitCode] duk_pcall_method(ctx, 1); // [retVal] duk_pop(ctx); // ... } } duk_context* ScriptEngine_Stop(MeshAgentHostContainer *agent, char *contextGUID) { duk_context *oldCtx = agent->meshCoreCtx; agent->meshCoreCtx = NULL; SCRIPT_ENGINE_SETTINGS *settings = ILibDuktape_ScriptContainer_GetSettings(oldCtx); duk_context *newCtx = ILibDuktape_ScriptContainer_InitializeJavaScriptEngineEx2(settings); ILibDuktape_MeshAgent_Init(newCtx, agent->chain, agent); ILibDuktape_SetNativeUncaughtExceptionHandler(newCtx, settings->nExeptionHandler, settings->nExceptionUserObject); duk_destroy_heap(oldCtx); agent->meshCoreCtx = newCtx; return(newCtx); } char* ScriptEngine_Restart(MeshAgentHostContainer *agent, char *contextGUID, char *buffer, int bufferLen) { duk_context *ctx = ScriptEngine_Stop(agent, contextGUID); if (ctx != NULL) { if (ILibDuktape_ScriptContainer_CompileJavaScript(ctx, buffer, bufferLen) != 0 || ILibDuktape_ScriptContainer_ExecuteByteCode(ctx) != 0) { sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "%s", (char*)duk_safe_to_string(ctx, -1)); duk_pop(ctx); return(ILibScratchPad); } return(NULL); } else { return "Restart Failed, because Script Engine Stop failed"; } } void MeshAgent_Slave_HeapWasDestroyed(duk_context *ctx, void *user) { MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user; agent->exitCode = ILibDuktape_Process_GetExitCode(ctx); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "MeshAgent_Slave: Engine has exited"); ILibStopChain(agent->chain); } void OnBrokenPipe_StdIn(ILibProcessPipe_Pipe sender) { ILibStopChain(((MeshAgentHostContainer*)((void**)ILibMemory_GetExtraMemory(sender, ILibMemory_ILibProcessPipe_Pipe_CONTAINERSIZE))[0])->chain); } // Called when we can now send more data to MeshCentral void MeshServer_OnSendOK(ILibWebClient_StateObject sender, void *user1, void *user2) { // TODO: Inform JavaScript core module that we are in underflow situation } // Called when the connection of the mesh server is fully authenticated void MeshServer_ServerAuthenticated(ILibWebClient_StateObject WebStateObject, MeshAgentHostContainer *agent) { int len = 0; MeshAgentDuktapePtrs *meshBindings; // Send the mesh agent tag to the server // We send the tag information independently of the meshcore because we could use this to select what meshcore to use on the server. ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AgentTag); // MeshCommand_AgentTag (15), agent tag information len = ILibSimpleDataStore_GetEx(agent->masterDb, "Tag", 3, ILibScratchPad2 + 2, sizeof(ILibScratchPad2) - 2); ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 2 + len, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); // Inform JavaScript core module of the connection // TODO: Verify with Bryan that only the core module will get this. No other modules should. if (agent->serverAuthState == 3) { meshBindings = agent->DuktapeMeshBindings; while (meshBindings != NULL) { if (meshBindings->OnConnect != NULL) { duk_push_heapptr(meshBindings->ctx, meshBindings->OnConnect); duk_push_heapptr(meshBindings->ctx, meshBindings->MeshAgentObject); duk_push_int(meshBindings->ctx, 1); // Argument 1 here indicates connection if (duk_pcall_method(meshBindings->ctx, 1) != 0) { ILibDuktape_Process_UncaughtException(meshBindings->ctx); } duk_pop(meshBindings->ctx); } meshBindings = meshBindings->Next; } } } // Process MeshCentral server commands. void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAgentHostContainer *agent, char *cmd, int cmdLen) { unsigned short command = ntohs(((unsigned short*)cmd)[0]); unsigned short requestid; MeshAgentDuktapePtrs *meshBindings; #ifndef MICROSTACK_NOTLS // If we are not authenticated with the mesh server, we only support auth commands. if (agent->serverAuthState != 3) { X509* peer = ILibWebClient_SslGetCert(WebStateObject); switch (command) { case MeshCommand_AuthRequest: // This is basic authentication information from the server, we need to sign this and return the signature. if (cmdLen == 66) { int signLen, certlen; SHA256_CTX c; EVP_PKEY *evp_prikey; RSA *rsa_prikey; char *certDer = ILibScratchPad2 + 4; // Hash the server's web certificate and check if it matches the one in the auth request util_keyhash2(peer, ILibScratchPad2); // Hash the server certificate public key and place it if (memcmp(ILibScratchPad2, cmd + 2, UTIL_HASHSIZE) != 0) { printf("Bad server certificate hash\r\n"); break; } // TODO: Disconnect memcpy_s(agent->serverNonce, sizeof(agent->serverNonce), cmd + 34, UTIL_HASHSIZE); // Place our certificate in the response ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AuthVerify); // MeshCommand_AuthVerify (2), agent certificate in ASN1 format certlen = i2d_X509(agent->selfcert.x509, (unsigned char **)&certDer); // Place the agent root certificate in DER form ((unsigned short*)ILibScratchPad2)[1] = htons(certlen); // Place the size of the certificate // Use our agent root private key to sign the ServerWebHash + ServerNonce + AgentNonce SHA256_Init(&c); util_keyhash2(peer, ILibScratchPad); SHA256_Update(&c, cmd + 2, UTIL_HASHSIZE); // Server web hash SHA256_Update(&c, agent->serverNonce, UTIL_HASHSIZE); // Server nonce SHA256_Update(&c, agent->agentNonce, UTIL_HASHSIZE); // Agent nonce SHA256_Final((unsigned char*)ILibScratchPad, &c); // Place the signature & send evp_prikey = agent->selfcert.pkey; rsa_prikey = EVP_PKEY_get1_RSA(evp_prikey); signLen = 65535 - (4 + certlen); if (RSA_sign(NID_sha256, (unsigned char*)ILibScratchPad, UTIL_HASHSIZE, (unsigned char*)(ILibScratchPad2 + 4 + certlen), (unsigned int*)&signLen, rsa_prikey) == 1) { // Signature succesful, send the result to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 4 + certlen + signLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); } RSA_free(rsa_prikey); } break; case MeshCommand_AuthVerify: // This is the signature from the server. We need to check everything is ok. if (cmdLen > 8) { unsigned short certlen = ntohs(((unsigned short*)cmd)[1]); if (cmdLen > (4 + certlen)) { int platformType = 1, hashlen = UTIL_HASHSIZE; SHA256_CTX c; X509* serverCert = NULL; EVP_PKEY *evp_pubkey; RSA *rsa_pubkey; char* ptr = cmd + 4; // Get the server certificate if (!d2i_X509(&serverCert, (const unsigned char**)&ptr, certlen)) { printf("Invalid server certificate\r\n"); break; } // TODO: Disconnect // Check if this certificate public key hash matches what we want // util_sha256((char*)(serverCert->cert_info->key->public_key->data), serverCert->cert_info->key->public_key->length, ILibScratchPad); // OpenSSL 1.0 X509_pubkey_digest(serverCert, EVP_sha256(), (unsigned char*)ILibScratchPad, (unsigned int*)&hashlen); // OpenSSL 1.1 if (memcmp(ILibScratchPad, agent->serverHash, UTIL_HASHSIZE) != 0) { printf("Server certificate mismatch\r\n"); break; } // TODO: Disconnect // Compute the authentication hash SHA256_Init(&c); util_keyhash2(peer, ILibScratchPad); SHA256_Update(&c, ILibScratchPad, UTIL_HASHSIZE); SHA256_Update(&c, agent->agentNonce, UTIL_HASHSIZE); SHA256_Update(&c, agent->serverNonce, UTIL_HASHSIZE); SHA256_Final((unsigned char*)ILibScratchPad, &c); // Verify the hash signature using the server certificate evp_pubkey = X509_get_pubkey(serverCert); rsa_pubkey = EVP_PKEY_get1_RSA(evp_pubkey); if (RSA_verify(NID_sha256, (unsigned char*)ILibScratchPad, UTIL_HASHSIZE, (unsigned char*)(cmd + 4 + certlen), cmdLen - (4 + certlen), rsa_pubkey) == 1) { int hostnamelen = (int)strnlen_s(agent->hostname, sizeof(agent->hostname)); // Server signature verified, we are good to go. agent->serverAuthState += 1; // Send to the server information about this agent (TODO: Replace this with a struct) if ((agent->batteryState != MeshAgentHost_BatteryInfo_NONE) && (agent->batteryState != MeshAgentHost_BatteryInfo_UNKNOWN)) { platformType = 2; } // If a battery is present, select laptop icon ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AuthInfo); // MeshCommand_AuthInfo (3), agent information ((unsigned int*)(ILibScratchPad2 + 2))[0] = htonl(1); // Agent information version ((unsigned int*)(ILibScratchPad2 + 6))[0] = htonl(MESH_AGENTID); // Agent Identifier ((unsigned int*)(ILibScratchPad2 + 10))[0] = htonl(agent->version); // Agent Version ((unsigned int*)(ILibScratchPad2 + 14))[0] = htonl(platformType); // Platfrom Type: This is the icon: 1 = Desktop, 2 = Laptop, 3 = Mobile, 4 = Server, 5 = Disk, 6 = Router memcpy_s(ILibScratchPad2 + 18, sizeof(ILibScratchPad2) - 18, agent->meshId, UTIL_HASHSIZE); // MeshId, taken from the agent settings ((unsigned int*)(ILibScratchPad2 + 50))[0] = htonl(8 + 16); // Capabilities of the agent(bitmask) : 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript ((unsigned short*)ILibScratchPad2 + 54)[0] = htons(hostnamelen); // Hostname length memcpy_s(ILibScratchPad2 + 56, sizeof(ILibScratchPad2) - 56, agent->hostname, hostnamelen); // Hostname // Send mesh agent information to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 56 + hostnamelen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); agent->retryTime = 0; printf("Connected.\n"); if (agent->serverAuthState == 3) { MeshServer_ServerAuthenticated(WebStateObject, agent); } } else { printf("Invalid server signature\r\n"); // TODO: Disconnect } RSA_free(rsa_pubkey); EVP_PKEY_free(evp_pubkey); X509_free(serverCert); } break; case MeshCommand_AuthConfirm: // Server indicates that we are authenticated, we can now send data. { // We have to wait for the server to indicate that it authenticated the agent (us) before sending any data to the server. // Node authentication requires the server make database calls, so we need to delay. agent->serverAuthState += 2; if (agent->serverAuthState == 3) { MeshServer_ServerAuthenticated(WebStateObject, agent); } } break; } } if (peer != NULL) { X509_free(peer); peer = NULL; } return; } #endif // If we get a authentication command after authentication is completed, ignore it. Commands 0 to 9 are reserved for authentication. if (command < 10) return; // If the command is JSON or binary over 1000, send it directly to the JavaScript code module // TODO: Verify with Bryan that only the core module will get this. No other modules should. if (cmd[0] == '{' || command >= 1000) { int processed = 0; // if (cmd[0] == '{') { cmd[cmdLen] = 0; printf("%s\r\n", cmd); } // DEBUG: Print JSON command meshBindings = agent->DuktapeMeshBindings; while (processed == 0 && meshBindings != NULL) { if (meshBindings->OnCommand != NULL) { duk_push_heapptr(meshBindings->ctx, meshBindings->OnCommand); // [func] duk_push_heapptr(meshBindings->ctx, meshBindings->MeshAgentObject); // [func][this] if (cmd[0] == '{') { // JSON duk_push_global_object(meshBindings->ctx); // [g] duk_get_prop_string(meshBindings->ctx, -1, "JSON"); // [g][JSON] duk_get_prop_string(meshBindings->ctx, -1, "parse"); // [g][JSON][func] duk_swap_top(meshBindings->ctx, -3); // [func][JSON][g] duk_pop_2(meshBindings->ctx); // [func] duk_push_lstring(meshBindings->ctx, cmd, cmdLen); // [func][str] if (duk_pcall(meshBindings->ctx, 1) != 0) { duk_pop(meshBindings->ctx); duk_push_lstring(meshBindings->ctx, cmd, cmdLen); } } else { // BINARY duk_push_external_buffer(meshBindings->ctx); // [func][this][buffer] duk_config_buffer(meshBindings->ctx, -1, cmd, cmdLen); } if (duk_pcall_method(meshBindings->ctx, 1) == 0) // [retVal] { if (duk_is_number(meshBindings->ctx, -1)) { processed = duk_get_int(meshBindings->ctx, -1); } // Get the return value } else { ILibDuktape_Process_UncaughtException(meshBindings->ctx); } duk_pop(meshBindings->ctx); // ... } meshBindings = meshBindings->Next; } return; } // All these commands must have both a commandid and a requestid if (cmdLen < 4) return; requestid = ntohs(((unsigned short*)cmd)[1]); // Process Core Module Commands here, but only if we aren't running a local script switch (command) { case MeshCommand_CoreModule: // New core modules to be used instead of the old one, if empty, remove the core module { char *coreException = NULL; // If the agent is running with a local core, ignore this command if (agent->localScript != 0) break; if (cmdLen > 36) // Setup a new mesh core. Command is: cmdid + requestid + sha256hash + javascript { char *hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA256 hash for the currently running code if (hashref == NULL || memcmp(hashref, cmd + 4, 32) != 0) { // If server sends us the same core, just do nothing. // Server sent us a new core, start by storing it in the data store ILibSimpleDataStore_PutEx(agent->masterDb, "CoreModule", 10, cmd + 36, cmdLen - 36); // Store the JavaScript in the data store hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA256 hash if (memcmp(hashref, cmd + 4, 32) != 0) { // Check the hash for sanity // Something went wrong, clear the data store ILibSimpleDataStore_Delete(agent->masterDb, "CoreModule"); // Stop the currently running core if present ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "MeshCore: Stop"); ScriptEngine_Stop(agent, MeshAgent_JavaCore_ContextGuid); // Tell the server we are no longer running a core module ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 4, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); break; } // Stop the current JavaScript core if present and launch the new one. // JavaScript located at (cmd + 36) of length (cmdLen - 36) //printf("CORE: Restart\r\n"); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "MeshCore: Restart"); if ((coreException = ScriptEngine_Restart(agent, MeshAgent_JavaCore_ContextGuid, cmd + 40, cmdLen - 40)) != NULL) { ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "MeshCore: Error: %s", coreException); // TODO: Ylian: New Java Core threw an exception... Exception String is stored in 'coreException' } // Since we did a big write to the data store, good time to compact the store ILibSimpleDataStore_Compact(agent->masterDb); } // Create the server confirmation message that we are running the new core ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id memcpy_s(ILibScratchPad2 + 4, sizeof(ILibScratchPad2) - 4, hashref, UTIL_HASHSIZE); // SHA256 hash // Send the confirmation to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 36, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); } else if (cmdLen == 4) { // Server is telling us to stop running a core ILibSimpleDataStore_Delete(agent->masterDb, "CoreModule"); // Clear the core from datastore // Stop the current JavaScript core if present and launch the new one. ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "MeshCore: Stop"); ScriptEngine_Stop(agent, MeshAgent_JavaCore_ContextGuid); // Confirm to the server that we are not running any core ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 4, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); } break; } case MeshCommand_CoreModuleHash: // Request/return the SHA256 hash of the core module { // Tell the server what core module we are running char *hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA256 hash int len = 4; // If the agent is running with a local core, ignore this command if (agent->localScript != 0) break; // Confirm to the server what core we are running ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id if (hashref != NULL) { memcpy_s(ILibScratchPad2 + 4, sizeof(ILibScratchPad2) - 4, hashref, UTIL_HASHSIZE); len += 32; } // Send the confirmation to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, len, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); break; } case MeshCommand_AgentHash: { if (agent->disableUpdate != 0) { break; } // This is a request for the hash of the agent binary // Built the response that includes our self hash ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AgentHash); // MeshCommand_AgentHash (12), SHA256 hash of the agent executable ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id memcpy_s(ILibScratchPad2 + 4, sizeof(ILibScratchPad2) - 4, agent->agentHash, UTIL_HASHSIZE);// SHA256 hash of the agent executable // Send the self hash back to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 36, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); break; } case MeshCommand_AgentUpdate: { #ifdef WIN32 char* updateFilePath = MeshAgent_MakeAbsolutePath(agent->exePath, ".update.exe"); #else char* updateFilePath = MeshAgent_MakeAbsolutePath(agent->exePath, ".update"); #endif char updateFileHash[32]; if (cmdLen == 4) { // Indicates the start of the agent update transfer util_deletefile(updateFilePath); } else if (cmdLen == 36) { // Indicates the end of the agent update transfer // Check the SHA256 hash of the received file against the file we got. if ((util_sha256file(updateFilePath, updateFileHash) == 0) && (memcmp(updateFileHash, cmd + 4, 32) == 0)) { printf("UPDATE: End OK\r\n"); // Check the file signature & version number //if (signcheck_verifysign(updateFilePath, 1)) { // Everything looks good, lets perform the update agent->performSelfUpdate = 1; ILibStopChain(agent->chain); } } else { // Hash check failed, delete the file and do nothing. On next server reconnect, we will try again. util_deletefile(updateFilePath); } } break; } case MeshCommand_AgentUpdateBlock: { // Write the mesh agent block to file #ifdef WIN32 char* updateFilePath = MeshAgent_MakeAbsolutePath(agent->exePath, ".update.exe"); #else char* updateFilePath = MeshAgent_MakeAbsolutePath(agent->exePath, ".update"); #endif // We have to try to write until it works, fopen sometimes fails while (util_appendfile(updateFilePath, cmd + 4, cmdLen - 4) == 0) { #ifdef WIN32 Sleep(100); #else sleep(100); #endif } // Confirm we got a mesh agent update block ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AgentUpdateBlock); // MeshCommand_AgentHash (14), SHA256 hash of the agent executable ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 4, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); break; } } } void MeshServer_ControlChannel_IdleTimeout(ILibWebClient_StateObject WebStateObject, void *user) { MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user; ILibWebClient_WebSocket_Ping(WebStateObject); ILibWebClient_SetTimeout(WebStateObject, agent->controlChannel_idleTimeout_seconds, MeshServer_ControlChannel_IdleTimeout, user); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore/MeshServer_ControlChannel_IdleTimeout(): Sending Ping"); } ILibWebClient_WebSocket_PingResponse MeshServer_ControlChannel_PingSink(ILibWebClient_StateObject WebStateObject, void *user) { return ILibWebClient_WebSocket_PingResponse_Respond; } void MeshServer_ControlChannel_PongSink(ILibWebClient_StateObject WebStateObject, void *user) { #ifdef _REMOTELOGGING MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user; ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore/MeshServer_ControlChannel_IdleTimeout(): Received Poing"); #endif } void MeshServer_OnResponse(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE) { MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user1; // Look at the various connection states and handle data if needed switch (recvStatus) { case ILibWebClient_ReceiveStatus_Partial: case ILibWebClient_ReceiveStatus_LastPartial: *beginPointer = endPointer; // ToDo: Buffer this data and send it up break; case ILibWebClient_ReceiveStatus_Connection_Established: // New connection established. { int idleLen; if ((idleLen = ILibSimpleDataStore_Get(agent->masterDb, "controlChannelIdleTimeout", NULL, 0)) != 0) { if (idleLen > 15) { agent->controlChannel_idleTimeout_seconds = DEFAULT_IDLE_TIMEOUT; } else { char idleBuffer[16]; idleBuffer[ILibSimpleDataStore_Get(agent->masterDb, "controlChannelIdleTimeout", idleBuffer, sizeof(idleBuffer))] = 0; agent->controlChannel_idleTimeout_seconds = atoi(idleBuffer); } } else { agent->controlChannel_idleTimeout_seconds = DEFAULT_IDLE_TIMEOUT; } agent->controlChannel = WebStateObject; // Set the agent MeshCentral server control channel ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "Control Channel Idle Timeout = %d seconds", agent->controlChannel_idleTimeout_seconds); ILibWebClient_SetTimeout(WebStateObject, agent->controlChannel_idleTimeout_seconds, MeshServer_ControlChannel_IdleTimeout, agent); ILibWebClient_WebSocket_SetPingPongHandler(WebStateObject, MeshServer_ControlChannel_PingSink, MeshServer_ControlChannel_PongSink, agent); #ifndef MICROSTACK_NOTLS X509* peer = ILibWebClient_SslGetCert(WebStateObject); agent->serverAuthState = 0; // We are not authenticated. Bitmask: 1 = Server Auth, 2 = Agent Auth. agent->serverConnectionState = 2; // Start authentication by sending a auth nonce & server TLS cert hash. // Send 256 bits SHA256 hash of TLS cert public key + 256 bits nonce util_random(32, agent->agentNonce); // Generate a new mesh agent connection nonce ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AuthRequest); // MeshCommand_AuthRequest (1), server hash + nonce util_keyhash2(peer, ILibScratchPad2 + 2); // Hash the server certificate public key and place it if (peer != NULL) { X509_free(peer); } memcpy_s(ILibScratchPad2 + 34, sizeof(ILibScratchPad2) - 34, agent->agentNonce, UTIL_HASHSIZE); // Place our mesh agent nonce ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 66, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); #endif break; } case ILibWebClient_ReceiveStatus_Complete: // Disconnection // If the channel had been authenticates, inform JavaScript core module that we are not disconnected #ifndef MICROSTACK_NOTLS if (agent->serverAuthState == 3) #endif { MeshAgentDuktapePtrs *meshBindings = agent->DuktapeMeshBindings; while (meshBindings != NULL) { if (meshBindings->OnConnect != NULL) { duk_push_heapptr(meshBindings->ctx, meshBindings->OnConnect); duk_push_heapptr(meshBindings->ctx, meshBindings->MeshAgentObject); duk_push_int(meshBindings->ctx, 0); // 0 here as second parameter indicates disconnection if (duk_pcall_method(meshBindings->ctx, 1) != 0) { ILibDuktape_Process_UncaughtException(meshBindings->ctx); } duk_pop(meshBindings->ctx); } meshBindings = meshBindings->Next; } } agent->controlChannel = NULL; // Set the agent MeshCentral server control channel agent->serverConnectionState = 0; break; case ILibWebClient_ReceiveStatus_MoreDataToBeReceived: // Data received // Process Mesh Agent commands MeshServer_ProcessCommand(WebStateObject, agent, bodyBuffer, endPointer); break; } // If there are no headers, this is a connection error. Log it and try again... if (header == NULL) { ILibRemoteLogging_printf(ILibChainGetLogger(ILibWebClient_GetChainFromWebStateObject(WebStateObject)), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "Agent Host Container: Mesh Server Connection Error, trying again later."); printf("Mesh Server Connection Error\n"); if (agent->multicastServerUrl != NULL) { free(agent->multicastServerUrl); agent->multicastServerUrl = NULL; } MeshServer_Connect(agent); return; } if (recvStatus != ILibWebClient_ReceiveStatus_Partial) { *beginPointer = endPointer; } // TODO: Confirm with Bryan that this is how partial data works } #ifdef MICROSTACK_PROXY void MeshServer_ConnectEx_Enumerate_Contexts(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user) { MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user; if (Key1 == NULL && ((SCRIPT_ENGINE_ISOLATION*)Data)->reserved != 0) { // This is a regular Duktape Context duk_context *ctx = (duk_context*)Data; ScriptContainerSettings *settings = ScriptEngine_GetSettings(ctx); if (settings != NULL && (settings->permissions & SCRIPT_ENGINE_NO_MESH_AGENT_ACCESS) == 0) { memcpy_s(&(ILibDuktape_GetNewGlobalTunnel(ctx)->proxyServer), sizeof(struct sockaddr_in6), agent->proxyServer, sizeof(struct sockaddr_in6)); } } } #endif void MeshServer_ConnectEx(MeshAgentHostContainer *agent) { int len, serverUrlLen; char *path; char *host; char *serverUrl; unsigned short port; struct sockaddr_in6 meshServer; ILibHTTPPacket *req; ILibWebClient_RequestToken reqToken; parser_result *rs; parser_result_field *f; // If this is called while we are in any connection state, just leave now. if (agent->serverConnectionState != 0) return; if (ILibIsChainBeingDestroyed(agent->chain) != 0) { return; } len = ILibSimpleDataStore_Get(agent->masterDb, "MeshServer", ILibScratchPad2, sizeof(ILibScratchPad2)); if (len == 0) { printf("No MeshCentral settings found, place .msh file with this executable and restart.\r\n"); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "agentcore: MeshServer URI not found"); return; } rs = ILibParseString(ILibScratchPad2, 0, len, ",", 1); if (agent->serverIndex == 0) { int rval; util_random(4, (char*)&rval); agent->serverIndex = (rval % rs->NumResults) + 1; } else { agent->serverIndex = (agent->serverIndex % rs->NumResults) + 1; //if (agent->serverIndex == 1) //{ // printf("Aborting...\r\n"); // ILibDestructParserResults(rs); // return; //} } f = ILibParseString_GetResultIndex(rs, agent->serverIndex); f->datalength = ILibTrimString(&(f->data), f->datalength); f->data[f->datalength] = 0; serverUrl = f->data; serverUrlLen = f->datalength; if (f->datalength > 5 && memcmp(f->data, "local", 5) == 0) { if (agent->multicastServerUrl != NULL) { serverUrl = agent->multicastServerUrl; serverUrlLen = strlen(serverUrl); } else { // Multicast discovery packet to try to find our server if ((agent->multicastDiscovery != NULL) && (ILibSimpleDataStore_Get(agent->masterDb, "ServerID", ILibScratchPad2, sizeof(ILibScratchPad2)) == 65)) { ILibMulticastSocket_Broadcast(agent->multicastDiscovery, ILibScratchPad2, 64, 1); } ILibDestructParserResults(rs); MeshServer_Connect(agent); return; } } if (serverUrlLen < sizeof(agent->serveruri)) { strcpy_s(agent->serveruri, sizeof(agent->serveruri), serverUrl); } else { agent->serveruri[0] = 0; } #ifndef MICROSTACK_NOTLS ILibParseUriResult result = ILibParseUri(serverUrl, &host, &port, &path, &meshServer); #else ILibParseUri(serverUrl, &host, &port, &path, &meshServer); #endif if (meshServer.sin6_family == AF_UNSPEC) { // Could not resolve host name if (ILibSimpleDataStore_GetEx(agent->masterDb, serverUrl, serverUrlLen, (char*)&meshServer, sizeof(struct sockaddr_in6)) == 0) { meshServer.sin6_family = AF_UNSPEC; ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "agentcore: Could not resolve: %s", ILibScratchPad); printf("agentcore: Could not resolve: %s\n", ILibScratchPad); } } else { // Successfully resolved host name struct sockaddr_in6 tempAddr; len = ILibSimpleDataStore_GetEx(agent->masterDb, serverUrl, serverUrlLen, (char*)&tempAddr, sizeof(struct sockaddr_in6)); if (len == 0) { // No entry in DB, so update the value ILibSimpleDataStore_PutEx(agent->masterDb, serverUrl, serverUrlLen, (char*)&meshServer, ILibInet_StructSize(&meshServer)); } else { // Entry exists, lets see if we need to update if (tempAddr.sin6_family != meshServer.sin6_family || memcmp(&tempAddr, &meshServer, ILibInet_StructSize(&meshServer)) != 0) { // Entry was different, so we need to update it ILibSimpleDataStore_PutEx(agent->masterDb, serverUrl, serverUrlLen, (char*)&meshServer, ILibInet_StructSize(&meshServer)); } } } printf("Connecting to: %s\n", serverUrl); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore: Attempting connection to: %s", serverUrl); ILibDestructParserResults(rs); if ((len = ILibSimpleDataStore_Get(agent->masterDb, "ServerID", ILibScratchPad2, sizeof(ILibScratchPad2))) == 0) { printf("ServerID entry not found in Db!\n"); return; } rs = ILibParseString(ILibScratchPad2, 0, len, ",", 1); f = ILibParseString_GetResultIndex(rs, agent->serverIndex); if (f == NULL) { // Invalid Server ID Count printf("ServerID Count Mismatch\r\n"); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore: ServerID Count Mismatch. Hash Count = %d, Server Index = %d", rs->NumResults, agent->serverIndex); ILibDestructParserResults(rs); return; } f->datalength = ILibTrimString(&(f->data), f->datalength); if (f->datalength / 2 > sizeof(agent->serverHash)) { printf("ServerID too big\r\n"); ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore: ServerID too big. Was %d bytes, but expected %d bytes", f->datalength / 2, sizeof(agent->serverHash) - 1); ILibDestructParserResults(rs); return; } util_hexToBuf(f->data, f->datalength, agent->serverHash); ILibDestructParserResults(rs); if (ILibSimpleDataStore_Get(agent->masterDb, "MeshID", ILibScratchPad, sizeof(ILibScratchPad)) == 0) { printf("MeshID entry not found in Db!\n"); return; } memcpy_s(agent->meshId, sizeof(agent->meshId), ILibScratchPad, UTIL_HASHSIZE); #ifndef MICROSTACK_NOTLS util_keyhash(agent->selfcert, agent->g_selfid); // Compute our own identifier using our certificate #endif req = ILibCreateEmptyPacket(); ILibSetVersion(req, "1.1", 3); ILibSetDirective(req, "GET", 3, path, (int)strnlen_s(path, serverUrlLen)); ILibAddHeaderLine(req, "Host", 4, host, (int)strnlen_s(host, serverUrlLen)); free(path); free(host); if (meshServer.sin6_family != AF_UNSPEC) { ILibWebClient_AddWebSocketRequestHeaders(req, 65535, MeshServer_OnSendOK); reqToken = ILibWebClient_PipelineRequest(agent->httpClientManager, (struct sockaddr*)&meshServer, req, MeshServer_OnResponse, agent, NULL); #ifndef MICROSTACK_NOTLS ILibWebClient_Request_SetHTTPS(reqToken, result == ILibParseUriResult_TLS ? ILibWebClient_RequestToken_USE_HTTPS : ILibWebClient_RequestToken_USE_HTTP); #endif if ((len = ILibSimpleDataStore_Get(agent->masterDb, "WebProxy", ILibScratchPad, sizeof(ILibScratchPad))) != 0) { #ifdef MICROSTACK_PROXY unsigned short proxyPort = 80; int delimiter = ILibString_LastIndexOf(ILibScratchPad, len, ":", 1); if (delimiter > 0) { ILibScratchPad[delimiter] = 0; ILibScratchPad[len] = 0; proxyPort = atoi(ILibScratchPad + delimiter + 1); } agent->proxyServer = ILibWebClient_SetProxy(reqToken, ILibScratchPad, proxyPort, NULL, NULL); if (agent->proxyServer != NULL) { memcpy_s(&(ILibDuktape_GetNewGlobalTunnel(agent->meshCoreCtx)->proxyServer), sizeof(struct sockaddr_in6), agent->proxyServer, sizeof(struct sockaddr_in6)); } #else ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore.MeshServer_ConnectEx(): Proxy Specified, but support was not enabled in this build"); #endif } agent->serverConnectionState = 1; // We are trying to connect } else { ILibDestructPacket(req); } } void MeshServer_Connect(MeshAgentHostContainer *agent) { unsigned int timeout; // If this is called while we are in any connection state, just leave now. if (agent->serverConnectionState != 0) return; util_random(sizeof(int), (char*)&timeout); agent->disableUpdate = ILibSimpleDataStore_Get(agent->masterDb, "disableUpdate", NULL, 0); if (agent->retryTime == 0) { agent->retryTime = (timeout % 1500) + 500; // Random value between 500 and 2000 MeshServer_ConnectEx(agent); } else { int delay; if (agent->retryTime >= 240000) { // Cap at around 4 minutes delay = agent->retryTime + (timeout % 120000); // Random value between 4 and 6 minutes } else { delay = agent->retryTime + (timeout % agent->retryTime); // Random value between current value and double the current value } printf("AutoRetry Connect in %d milliseconds\n", delay); ILibLifeTime_AddEx(ILibGetBaseTimer(agent->chain), agent, delay, (ILibLifeTime_OnCallback)MeshServer_ConnectEx, NULL); agent->retryTime = delay; } } #ifndef MICROSTACK_NOTLS int ValidateMeshServer(ILibWebClient_RequestToken sender, int preverify_ok, STACK_OF(X509) *certs, struct sockaddr_in6 *address) { // Server validation is always true here. We will do a second round within the websocket to see if the server is really valid or not. return 1; } #endif void importSettings(MeshAgentHostContainer *agent, char* fileName) { int eq; char* importFile; int importFileLen; parser_result *pr; parser_result_field *f; importFileLen = ILibReadFileFromDiskEx(&importFile, fileName); if (importFileLen == 0) { return; } //printf("Importing settings file: %s\n", fileName); pr = ILibParseString(importFile, 0, importFileLen, "\n", 1); f = pr->FirstResult; while (f != NULL) { f->datalength = ILibTrimString(&(f->data), f->datalength); if (f->data[0] != 35) // Checking to see if this line is commented out { eq = ILibString_IndexOf(f->data, f->datalength, "=", 1); if (eq > 0) { char *key, *val; int keyLen, valLen; key = f->data; keyLen = eq; key[keyLen] = 0; val = key + keyLen + 1; valLen = f->datalength - keyLen - 1; if (val[valLen - 1] == 13) { --valLen; } valLen = ILibTrimString(&val, valLen); if (valLen == 0) { // Empty key, remove the value completely. ILibSimpleDataStore_DeleteEx(agent->masterDb, key, keyLen); } else { if (ntohs(((unsigned short*)val)[0]) == HEX_IDENTIFIER) { // HEX value ILibSimpleDataStore_PutEx(agent->masterDb, key, keyLen, ILibScratchPad2, util_hexToBuf(val + 2, valLen - 2, ILibScratchPad2)); } else { // STRING value val[valLen] = 0; ILibSimpleDataStore_PutEx(agent->masterDb, key, keyLen, val, valLen + 1); } } //printf("...Imported: %s\n", key); } } f = f->NextResult; } ILibDestructParserResults(pr); free(importFile); } void agentDumpKeysSink(ILibSimpleDataStore sender, char* Key, int KeyLen, void *user) { if (KeyLen < sizeof(ILibScratchPad2)) { memcpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), Key, KeyLen); ILibScratchPad2[KeyLen] = 0; printf("--> %s\n", ILibScratchPad2); } } MeshAgentHostContainer* MeshAgent_Create() { MeshAgentHostContainer* retVal = (MeshAgentHostContainer*)ILibMemory_Allocate(sizeof(MeshAgentHostContainer), 0, NULL, NULL); #ifdef WIN32 SYSTEM_POWER_STATUS stats; #endif retVal->agentID = (AgentIdentifiers)MESH_AGENTID; retVal->chain = ILibCreateChainEx(3 * sizeof(void*)); retVal->pipeManager = ILibProcessPipe_Manager_Create(retVal->chain); gethostname(retVal->hostname, (int)sizeof(retVal->hostname)); #ifdef WIN32 GetSystemPowerStatus(&stats); switch (stats.BatteryFlag) { case 128: retVal->batteryState = MeshAgentHost_BatteryInfo_NONE; break; case 255: retVal->batteryState = MeshAgentHost_BatteryInfo_UNKNOWN; break; default: retVal->batteryState = (MeshAgentHost_BatteryInfo)stats.BatteryFlag; break; } #endif ((void**)ILibMemory_GetExtraMemory(retVal->chain, ILibMemory_CHAIN_CONTAINERSIZE))[2] = retVal; return retVal; } void MeshAgent_Slave(MeshAgentHostContainer *agentHost) { // We are just a slave container agentHost->exitCode = ILibDuktape_ScriptContainer_StartSlave(agentHost->chain, agentHost->pipeManager); agentHost->chain = NULL; } void MeshAgent_ChainEnd(void *chain, void *user) { MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user; duk_destroy_heap(agent->meshCoreCtx); } void MeshAgent_RunScriptOnly_Finalizer(duk_context *ctx, void *user) { MeshAgentHostContainer *agentHost = (MeshAgentHostContainer*)user; if (ILibIsChainBeingDestroyed(agentHost->chain) == 0) { MeshAgent_Stop(agentHost); } } void MeshAgent_CoreModule_UncaughtException(duk_context *ctx, char *msg, void *user) { } #ifndef _NOHECI void MicroLMS_OnNotificationEx(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user) { struct cimAlertIndication *values = (struct cimAlertIndication*)((void**)user)[0]; char *xml = (char*)((void**)user)[1]; int xmllen = ((int*)((void**)user)[2])[0]; if (Key1 == NULL && ((SCRIPT_ENGINE_ISOLATION*)Data)->reserved != 0) { // This is a regular Duktape Context ILibDuktape_EventEmitter *emitter = MeshAgent_GetEventEmitter_FromCTX((duk_context*)Data); if (emitter != NULL) { duk_push_heapptr(emitter->ctx, emitter->object); // [MeshAgent] duk_get_prop_string(emitter->ctx, -1, "emit"); // [MeshAgent][emit] duk_swap_top(emitter->ctx, -2); // [emit][this] duk_push_string(emitter->ctx, "lmsNotification"); // [emit][this][event] duk_push_object(emitter->ctx); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, values->MessageID.data, values->MessageID.dataLen); // [emit][this][event][obj][msgId] duk_put_prop_string(emitter->ctx, -2, "messageId"); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, values->IndicationTime.data, values->IndicationTime.dataLen); // [emit][this][event][obj][time] duk_put_prop_string(emitter->ctx, -2, "indicationTime"); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, values->MessageArguments.data, values->MessageArguments.dataLen);// [emit][this][event][obj][msg] duk_put_prop_string(emitter->ctx, -2, "messageArguments"); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, xml, xmllen); // [emit][this][event][obj][rawXML] duk_put_prop_string(emitter->ctx, -2, "rawXML"); // [emit][this][event][obj] if (duk_pcall_method(emitter->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(emitter->ctx, "MeshAgent.onLmsNotification(): Error "); } duk_pop(emitter->ctx); // ... } } } void MicroLMS_OnNotification(void *module, struct cimAlertIndication *values, char* xml, int xmllen) { MeshAgentHostContainer *agent = (MeshAgentHostContainer*)((void**)ILibMemory_GetExtraMemory(module, ILibMemory_ILibLMS_CONTAINERSIZE))[0]; if (agent->meshCoreCtx != NULL) { ILibDuktape_EventEmitter *emitter = MeshAgent_GetEventEmitter_FromCTX(agent->meshCoreCtx); if (emitter != NULL) { duk_push_heapptr(emitter->ctx, emitter->object); // [MeshAgent] duk_get_prop_string(emitter->ctx, -1, "emit"); // [MeshAgent][emit] duk_swap_top(emitter->ctx, -2); // [emit][this] duk_push_string(emitter->ctx, "lmsNotification"); // [emit][this][event] duk_push_object(emitter->ctx); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, values->MessageID.data, values->MessageID.dataLen); // [emit][this][event][obj][msgId] duk_put_prop_string(emitter->ctx, -2, "messageId"); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, values->IndicationTime.data, values->IndicationTime.dataLen); // [emit][this][event][obj][time] duk_put_prop_string(emitter->ctx, -2, "indicationTime"); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, values->MessageArguments.data, values->MessageArguments.dataLen);// [emit][this][event][obj][msg] duk_put_prop_string(emitter->ctx, -2, "messageArguments"); // [emit][this][event][obj] duk_push_lstring(emitter->ctx, xml, xmllen); // [emit][this][event][obj][rawXML] duk_put_prop_string(emitter->ctx, -2, "rawXML"); // [emit][this][event][obj] if (duk_pcall_method(emitter->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(emitter->ctx, "MeshAgent.onLmsNotification(): Error "); } duk_pop(emitter->ctx); // ... } } } #endif void MeshAgent_AgentMode_IPAddressChanged_Handler(ILibIPAddressMonitor sender, void *user) { MeshAgentHostContainer *agentHost = (MeshAgentHostContainer*)user; if (agentHost->serverConnectionState == 0) { ILibRemoteLogging_printf(ILibChainGetLogger(agentHost->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "Network State Change detected... Resetting connection timeout"); ILibLifeTime_Remove(ILibGetBaseTimer(agentHost->chain), agentHost); agentHost->retryTime = 3000; ILibLifeTime_AddEx(ILibGetBaseTimer(agentHost->chain), agentHost, agentHost->retryTime, (ILibLifeTime_OnCallback)MeshServer_ConnectEx, NULL); } } #ifndef WIN32 int MeshAgent_AgentMode(MeshAgentHostContainer *agentHost, int paramLen, char **param, int parseCommands); void MeshAgent_AgentMost_dbRetryCallback(void *object) { MeshAgentHostContainer *agentHost = (MeshAgentHostContainer*)((void**)object)[0]; int paramLen = ((int*)&((void**)object)[1])[0]; char **param = (char**)((void**)object)[2]; int parseCommands = ((int*)&((void**)object)[3])[0]; if (MeshAgent_AgentMode(agentHost, paramLen, param, parseCommands) == 0) { ILibStopChain(agentHost->chain); } } #endif int MeshAgent_AgentMode(MeshAgentHostContainer *agentHost, int paramLen, char **param, int parseCommands) { int resetNodeId = 0; //int x; //size_t tmpLen; #ifdef WIN32 int pLen; #endif #ifdef _POSIX int options = 0; #endif // We are a Mesh Agent printf("MeshCentral2 Agent\r\n"); // MESH_AGENT_VERSION if (agentHost->masterDb == NULL) { agentHost->masterDb = ILibSimpleDataStore_Create(MeshAgent_MakeAbsolutePath(agentHost->exePath, ".db")); } #ifndef WIN32 if (agentHost->masterDb == NULL) { void **data = (void**)ILibScratchPad; data[0] = agentHost; ((int*)&(data[1]))[0] = paramLen; data[2] = param; ((int*)&(data[3]))[0] = parseCommands; switch (agentHost->dbRetryCount) { case 10: printf("Unable to open database... Giving up...\r\n"); return(0); default: printf("Unable to open database... Trying again later...\r\n"); agentHost->dbRetryCount++; ILibLifeTime_AddEx(ILibGetBaseTimer(agentHost->chain), data, 2000, MeshAgent_AgentMost_dbRetryCallback, NULL); return(1); } } #else if (agentHost->masterDb == NULL) { printf("Unable to open database...\r\n"); return(0); } #endif agentHost->httpClientManager = ILibCreateWebClient(3, agentHost->chain); ILibRemoteLogging_printf(ILibChainGetLogger(agentHost->chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "agentcore: argv[0] = %s", param[0]); #if defined(_WINSERVICE) // If running as a windows services, check the "ResetNodeId" key. { HKEY hKey; DWORD len = 0; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Open Source\\MeshAgent2"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) { if (RegQueryValueExA(hKey, TEXT("ResetNodeId"), NULL, NULL, NULL, &len) == ERROR_SUCCESS && len > 0) { if (RegDeleteValue(hKey, TEXT("ResetNodeId")) == ERROR_SUCCESS) { resetNodeId = 1; } // Force certificate reset } RegCloseKey(hKey); } } #else // If running in console mode, check the --resetnodeid command switch if (parseCommands != 0) { int i; // Parse command-line arguments for (i = 0; i < paramLen; ++i) { if (strcmp(param[i], "--resetnodeid") == 0) { resetNodeId = 1; } } } #endif #ifndef MICROSTACK_NOTLS // Load the mesh agent certificates if ((resetNodeId == 1 || agent_LoadCertificates(agentHost) != 0) && agent_GenerateCertificates(agentHost, NULL) != 0) { printf("Certificate error\r\n"); } if (agent_VerifyMeshCertificates(agentHost) != 0) { printf("Certificate validation error\r\n"); } #else printf("TLS support disabled\n"); #endif // Read the .tag file if present and push it into the database { char* str = NULL; int len = (int)util_readfile(MeshAgent_MakeAbsolutePath(agentHost->exePath, ".tag"), &str, 1024); if (str != NULL) { ILibSimpleDataStore_PutEx(agentHost->masterDb, "Tag", 3, str, len); free(str); } else { ILibSimpleDataStore_DeleteEx(agentHost->masterDb, "Tag", 3); } } // Read the .proxy file if present and push it into the database { char* str = NULL; int len = (int)util_readfile(MeshAgent_MakeAbsolutePath(agentHost->exePath, ".proxy"), &str, 1024); if (str != NULL) { ILibSimpleDataStore_PutEx(agentHost->masterDb, "WebProxy", 8, str, len); free(str); } else { ILibSimpleDataStore_DeleteEx(agentHost->masterDb, "WebProxy", 8); } } #ifndef _NOHECI #ifdef WIN32 // Check if the LMS service is installed in the service manager if (GetServiceState(TEXT("LMS")) == 100) { // If not, setup MicroLMS (Intel AMT interface service) if it's needed if ((agentHost->microLMS = ILibLMS_CreateEx(agentHost->chain, agentHost->exePath, MicroLMS_OnNotification, sizeof(void*))) != NULL) MSG("MicroLMS activated\r\n"); } #else // Setup MicroLMS (Intel AMT interface service) if it's needed if ((agentHost->microLMS = ILibLMS_CreateEx(agentHost->chain, agentHost->exePath, MicroLMS_OnNotification, sizeof(void*))) != NULL) MSG("MicroLMS activated\r\n"); #endif if (agentHost->microLMS != NULL) { ((void**)ILibMemory_GetExtraMemory(agentHost->microLMS, ILibMemory_ILibLMS_CONTAINERSIZE))[0] = agentHost; } #endif // Check to see if we need to import a settings file importSettings(agentHost, MeshAgent_MakeAbsolutePath(agentHost->exePath, ".msh")); #ifdef WIN32 // If running as a Windows service, set basic values to the registry, this allows other applications to know what the mesh agent is doing. { HKEY hKey; #if defined(_WINSERVICE) // If running as a Windows Service, save the key in LOCAL_MACHINE if (RegCreateKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Open Source\\MeshAgent2"), &hKey) == ERROR_SUCCESS) #else // If running in Console mode, save the key in CURRENT_USER if (RegCreateKey(HKEY_CURRENT_USER, TEXT("Software\\Open Source\\MeshAgent2"), &hKey) == ERROR_SUCCESS) #endif { // Save the NodeId util_tohex(agentHost->g_selfid, UTIL_HASHSIZE, ILibScratchPad); RegSetValueExA(hKey, "NodeId", 0, REG_SZ, ILibScratchPad, (int)strlen(ILibScratchPad)); // Save the AgentHash util_tohex(agentHost->agentHash, UTIL_HASHSIZE, ILibScratchPad); RegSetValueExA(hKey, "AgentHash", 0, REG_SZ, ILibScratchPad, (int)strlen(ILibScratchPad)); // Save a bunch of values in the registry if (ILibSimpleDataStore_Get(agentHost->masterDb, "MeshID", NULL, 0) == 0) { RegDeleteKeyA(hKey, "MeshId"); } else { util_tohex(ILibScratchPad2, ILibSimpleDataStore_Get(agentHost->masterDb, "MeshID", ILibScratchPad2, (int)sizeof(ILibScratchPad2)), ILibScratchPad); RegSetValueExA(hKey, "MeshId", 0, REG_SZ, ILibScratchPad, (int)strlen(ILibScratchPad)); } // Save the MeshId if ((pLen = ILibSimpleDataStore_Get(agentHost->masterDb, "MeshServer", ILibScratchPad2, (int)sizeof(ILibScratchPad2))) == 0) { RegDeleteKeyA(hKey, "MeshServerUrl"); } else { RegSetValueExA(hKey, "MeshServerUrl", 0, REG_SZ, (BYTE*)ILibScratchPad2, (int)strlen(ILibScratchPad2)); } // Save the mesh server URL if ((pLen = ILibSimpleDataStore_Get(agentHost->masterDb, "ServerID", ILibScratchPad2, (int)sizeof(ILibScratchPad2))) == 0) { RegDeleteKeyA(hKey, "MeshServerId"); } else { RegSetValueExA(hKey, "MeshServerId", 0, REG_SZ, (BYTE*)ILibScratchPad2, (int)strlen(ILibScratchPad2)); } // Save the mesh server id if ((pLen = ILibSimpleDataStore_Get(agentHost->masterDb, "WebProxy", ILibScratchPad2, (int)sizeof(ILibScratchPad2))) == 0) { RegDeleteKeyA(hKey, "Proxy"); } else { RegSetValueExA(hKey, "Proxy", 0, REG_SZ, (BYTE*)ILibScratchPad2, (int)strlen(ILibScratchPad2)); } // Save the proxy if ((pLen = ILibSimpleDataStore_Get(agentHost->masterDb, "Tag", ILibScratchPad2, (int)sizeof(ILibScratchPad2))) == 0) { RegDeleteKeyA(hKey, "Tag"); } else { RegSetValueExA(hKey, "Tag", 0, REG_SZ, (BYTE*)ILibScratchPad2, (int)strlen(ILibScratchPad2)); } // Save the tag // Close the registry key RegCloseKey(hKey); } } #endif #ifndef MICROSTACK_NOTLS ILibWebClient_EnableHTTPS(agentHost->httpClientManager, &(agentHost->selftlscert), agentHost->selfcert.x509, ValidateMeshServer); #endif #ifdef _POSIX if (paramLen >= 2) { if ((strcmp(param[1], "start") == 0 || strcmp(param[1], "-d") == 0)) options = 1; else if ((strcmp(param[1], "stop") == 0 || strcmp(param[1], "-s") == 0)) options = 2; else if ((strcmp(param[1], "restart") == 0 || strcmp(param[1], "-r") == 0)) options = 3; } // Stop if (options & 2) { FILE *fd = NULL; char str[15]; pid_t pid = 0; size_t len; /* #if defined(__APPLE__) && defined(_DAEMON) && defined(_LINKVM) installOsx(0); #endif */ #ifdef _ANDROID fd = fopen("/data/usr/tmp/meshagent.pid", "r"); #else fd = fopen("/var/run/meshagent.pid", "r"); #endif if (fd == NULL) fd = fopen(".meshagent.pid", "r"); if (fd != NULL) { len = fread(str, sizeof(char), 15, fd); if (len > 0) { sscanf(str, "%d\r\n", &pid); if (pid > 0 && kill(pid, SIGKILL) == 0) printf("Mesh agent stopped.\r\n"); else printf("Mesh agent not running.\r\n"); #ifdef _ANDROID remove("/data/usr/tmp/meshagent.pid"); #else remove("/var/run/meshagent.pid"); remove(".meshagent.pid"); #endif } fclose(fd); } else { printf("Unable to find process id file.\r\n"); } } // Start if (options & 1) { FILE *fd = NULL; char str[15]; pid_t pid, sid; size_t len; /* #if defined(__APPLE__) && defined(_DAEMON) && defined(_LINKVM) installOsx(1); #endif */ pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } else if (pid > 0) { len = snprintf(str, 15, "%d\r\n", pid); #ifdef _ANDROID fd = fopen("/data/usr/tmp/meshagent.pid", "w"); #else fd = fopen("/var/run/meshagent.pid", "w"); #endif if (fd == NULL) fd = fopen(".meshagent.pid", "w"); if (fd != NULL) { if (fwrite(str, sizeof(char), len, fd)) {} fclose(fd); } printf("Mesh agent started.\r\n"); exit(EXIT_SUCCESS); } sid = setsid(); if (sid < 0) { exit(EXIT_FAILURE); } close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); parseCommands = 0; } else if (options) exit(EXIT_SUCCESS); #endif if (parseCommands == 0 || paramLen == 1 || ((paramLen == 2) && (strcmp(param[1], "run") == 0))) { #ifdef WIN32 char* updateFilePath = MeshAgent_MakeAbsolutePath(agentHost->exePath, ".update.exe"); #else char* updateFilePath = MeshAgent_MakeAbsolutePath(agentHost->exePath, ".update"); #endif // Delete the mesh agent update file if there is one util_deletefile(updateFilePath); // Check if there is a CoreModule in the db char *CoreModule; int CoreModuleLen = agentHost->localScript == 0 ? ILibSimpleDataStore_Get(agentHost->masterDb, "CoreModule", NULL, 0) : 0; MeshAgentDuktapePtrs* ptrs = agentHost->DuktapeMeshBindings; while (ptrs != NULL) { if (ptrs->OnReady != NULL) { duk_push_heapptr(ptrs->ctx, ptrs->OnReady); // [func] duk_push_heapptr(ptrs->ctx, ptrs->MeshAgentObject); // [func][this] if (duk_pcall_method(ptrs->ctx, 0) != 0) {ILibDuktape_Process_UncaughtException(ptrs->ctx); } // [retVal] duk_pop(ptrs->ctx); // ... } ptrs = ptrs->Next; } if (agentHost->localScript == 0) { // Create the context for the Local CoreModule, regardless if we have one yet agentHost->meshCoreCtx = ILibDuktape_ScriptContainer_InitializeJavaScriptEngineEx(0, 0, agentHost->chain, NULL, agentHost->masterDb, agentHost->exePath, agentHost->pipeManager, NULL, NULL); ILibDuktape_MeshAgent_Init(agentHost->meshCoreCtx, agentHost->chain, agentHost); ILibDuktape_SetNativeUncaughtExceptionHandler(agentHost->meshCoreCtx, MeshAgent_CoreModule_UncaughtException, agentHost); if (CoreModuleLen > 0) { // There is a core module, launch it now. CoreModule = (char*)ILibMemory_Allocate(CoreModuleLen, 0, NULL, NULL); ILibSimpleDataStore_Get(agentHost->masterDb, "CoreModule", CoreModule, CoreModuleLen); if (ILibDuktape_ScriptContainer_CompileJavaScript(agentHost->meshCoreCtx, CoreModule + 4, CoreModuleLen - 4) != 0 || ILibDuktape_ScriptContainer_ExecuteByteCode(agentHost->meshCoreCtx) != 0) { ILibRemoteLogging_printf(ILibChainGetLogger(agentHost->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "Error Executing MeshCore: %s", duk_safe_to_string(agentHost->meshCoreCtx, -1)); duk_pop(agentHost->meshCoreCtx); } free(CoreModule); } } ILibIPAddressMonitor_Create(agentHost->chain, MeshAgent_AgentMode_IPAddressChanged_Handler, agentHost); MeshServer_Connect(agentHost); // We are acting as a mesh agent if (((ILibSimpleDataStore_Get(agentHost->masterDb, "MeshServer", ILibScratchPad, sizeof(ILibScratchPad))) > 5) && (memcmp(ILibScratchPad, "local", 5) == 0)) { // Mesh agent is in local mode, start the multicast server discovery struct sockaddr_in multicastAddr4; struct sockaddr_in6 multicastAddr6; // Cleanup all addresses memset(&multicastAddr4, 0, sizeof(struct sockaddr_in)); memset(&multicastAddr6, 0, sizeof(struct sockaddr_in6)); // Setup addresses if (ILibDetectIPv6Support()) { // IPv6 support multicastAddr6.sin6_family = AF_INET6; multicastAddr6.sin6_port = htons(16989); ILibInet_pton(AF_INET6, MESH_MCASTv6_GROUP, &(multicastAddr6.sin6_addr)); } // Setup multicastAddr4 multicastAddr4.sin_family = AF_INET; multicastAddr4.sin_port = htons(16989); ILibInet_pton(AF_INET, MESH_MCASTv4_GROUP, &(multicastAddr4.sin_addr)); agentHost->multicastDiscovery = ILibMulticastSocket_Create(agentHost->chain, 1024, MESH_AGENT_PORT, &multicastAddr4, &multicastAddr6, UDPSocket_OnData, agentHost, 1); if (agentHost->multicastDiscovery == NULL) { ILIBMARKPOSITION(219); return 1; } } return 1; } return 0; } void MeshAgent_ScriptMode_UncaughtExceptionSink(duk_context *ctx, char *msg, void *user) { printf("*** UNCAUGHT EXCEPTION: %s ***\n", msg); //ScriptEngine_Exit((MeshAgentHostContainer*)user, MeshAgent_JavaCore_ContextGuid, 254); } void MeshAgent_ScriptMode(MeshAgentHostContainer *agentHost, int argc, char **argv) { char *jsFile; int jsFileLen; int i; unsigned int execTimeout = 0; unsigned int secFlags = 0; char **scriptArgs = NULL; char *jsPath; int sx = 1; int connectAgent = 0; int pathLen = 0; // Get the full path name of the JavaScript file #ifdef WIN32 pathLen = GetFullPathName(argv[1], sizeof(ILibScratchPad2), ILibScratchPad2, NULL); #else if (realpath(argv[1], ILibScratchPad2) != NULL) { pathLen = strnlen_s(ILibScratchPad2, PATH_MAX); } #endif // Try to load the JavaScript file from disk, if fail, return jsFileLen = ILibReadFileFromDiskEx(&jsFile, ILibScratchPad2); if (jsFileLen == 0) { printf("ERROR loading %s\n", ILibScratchPad2); return; } // We need to pass the JavaScript full path to the JavaScript runtime as the first argument. Set the up here. scriptArgs = (char**)ILibMemory_Allocate((1 + argc) * sizeof(char*), 1 + pathLen, NULL, (void**)&jsPath); // KLOCWORK is being dumb, becuase ILibScratchpad2 is gauranteed to be NULL terminated strncpy_s(jsPath, ILibMemory_GetExtraMemorySize(jsPath), ILibScratchPad2, ILibMemory_GetExtraMemorySize(jsPath)); scriptArgs[0] = jsPath; #ifdef WIN32 i = ILibString_LastIndexOf(ILibScratchPad2, pathLen, "\\", 1); #else i = ILibString_LastIndexOf(ILibScratchPad2, pathLen, "/", 1); #endif ILibScratchPad2[i] = 0; #ifdef WIN32 SetCurrentDirectory(ILibScratchPad2); #else ignore_result(chdir(ILibScratchPad2)); #endif // Parse arguments. Handle the ones we can, others will be passed to the JavaScript engine. for (i = 2; i < argc; ++i) { if(agentHost->masterDb == NULL && strncmp(argv[i], "--script-db", 11) == 0 && ((i + 1) < argc)) { // Specify DB file path agentHost->masterDb = ILibSimpleDataStore_Create(MeshAgent_MakeAbsolutePath(agentHost->exePath, argv[i + 1])); ++i; } else if (strncmp(argv[i], "--script-flags", 14) == 0 && ((i + 1) < argc)) { // JS Permissions (see .h for values) if (ntohs(((unsigned short*)argv[i + 1])[0]) == HEX_IDENTIFIER) { int xlen = (int)strnlen_s(argv[i + 1], 32); if (xlen <= 10) { util_hexToBuf(argv[i + 1] + 2, xlen - 2, (char*)&secFlags); secFlags = ntohl(secFlags); } ++i; } } else if (strncmp(argv[i], "--script-timeout", 16) == 0 && ((i + 1) < argc)) { // Seconds before watchdog termination, 0 for unlimited execTimeout = (unsigned int)atoi(argv[i + 1]); ++i; } else if (strncmp(argv[i], "--script-connect", 16) == 0) { // Connect to MeshCentral connectAgent = 1; if (agentHost->masterDb == NULL) { agentHost->masterDb = ILibSimpleDataStore_Create(MeshAgent_MakeAbsolutePath(agentHost->exePath, ".db")); } } else { // Unhandled arguments, passed to JavaScript scriptArgs[sx++] = argv[i]; } } // Start the JavaScript engine, run the loaded .js file agentHost->localScript = 1; agentHost->meshCoreCtx = ILibDuktape_ScriptContainer_InitializeJavaScriptEngineEx(secFlags, execTimeout, agentHost->chain, scriptArgs, connectAgent != 0 ? agentHost->masterDb : NULL, agentHost->exePath, agentHost->pipeManager, connectAgent == 0 ? MeshAgent_RunScriptOnly_Finalizer : NULL, agentHost); ILibDuktape_SetNativeUncaughtExceptionHandler(agentHost->meshCoreCtx, MeshAgent_ScriptMode_UncaughtExceptionSink, agentHost); if (connectAgent != 0) { ILibDuktape_MeshAgent_Init(agentHost->meshCoreCtx, agentHost->chain, agentHost); } if (ILibDuktape_ScriptContainer_CompileJavaScript(agentHost->meshCoreCtx, jsFile, jsFileLen) != 0 || ILibDuktape_ScriptContainer_ExecuteByteCode(agentHost->meshCoreCtx) != 0) { // Error ILibRemoteLogging_printf(ILibChainGetLogger(agentHost->chain), ILibRemoteLogging_Modules_Microstack_Generic | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "Script Error: %s", duk_safe_to_string(agentHost->meshCoreCtx, -1)); duk_pop(agentHost->meshCoreCtx); } // JavaScript copies this, we do not need this anymore. free(jsFile); free(scriptArgs); // If in agent mode, setup the chain to be a mesh agent if (connectAgent != 0) { if (MeshAgent_AgentMode(agentHost, argc, argv, 0) == 0) { ILibStopChain(agentHost->chain); // Agent Error, stop the chain } } } void MeshAgent_ScriptMode_Dispatched(void *chain, void *user) { MeshAgent_ScriptMode((MeshAgentHostContainer*)((void**)user)[0], ((int*)((void**)user)[1])[0], (char**)((void**)user)[2]); } void MeshAgent_AgentMode_Dispatched(void *chain, void *user) { if (MeshAgent_AgentMode((MeshAgentHostContainer*)((void**)user)[0], ((int*)((void**)user)[1])[0], (char**)((void**)user)[2], 1) == 0) { ILibStopChain(((MeshAgentHostContainer*)((void**)user)[0])->chain); } } int MeshAgent_Start(MeshAgentHostContainer *agentHost, int paramLen, char **param) { char exePath[1024]; #ifdef WIN32 int x; #elif defined(__APPLE__) int len = 1024; #elif defined(NACL) // Do nothing #else int x; #endif if ((paramLen == 1 && strcmp(param[0], "--slave") == 0) || (paramLen == 2 && strcmp(param[1], "--slave") == 0)) { MeshAgent_Slave(agentHost); return 0; } if (paramLen == 2 && strcmp(param[1], "--netinfo") == 0) { char* data; int len = MeshInfo_GetSystemInformation(&data); if (len > 0) { printf("%s\r\n", data); } return 0; } agentHost->exePath = exePath; exePath[0] = 0; #ifdef WIN32 GetModuleFileName(NULL, exePath, sizeof(exePath)); #elif defined(__APPLE__) if (_NSGetExecutablePath(exePath, &len) != 0) ILIBCRITICALEXIT(247); exePath[len] = 0; agentHost->exePath = exePath; #elif defined(NACL) #else x = readlink("/proc/self/exe", exePath, 1024); if (x < 0 || x >= 1024) ILIBCRITICALEXIT(246); exePath[x] = 0; #endif // Perform a self SHA256 Hash util_sha256file(agentHost->exePath, agentHost->agentHash); #ifdef _REMOTELOGGINGSERVER { int len; if (agentHost->masterDb == NULL) { agentHost->masterDb = ILibSimpleDataStore_Create(MeshAgent_MakeAbsolutePath(agentHost->exePath, ".db")); } if (agentHost->masterDb != NULL) { if ((len = ILibSimpleDataStore_Get(agentHost->masterDb, "enableILibRemoteLogging", ILibScratchPad, sizeof(ILibScratchPad))) != 0) { ILibScratchPad[len] = 0; ILibStartDefaultLoggerEx(agentHost->chain, (unsigned short)atoi(ILibScratchPad), MeshAgent_MakeAbsolutePath(agentHost->exePath, ".wlg")); } ILibSimpleDataStore_Close(agentHost->masterDb); agentHost->masterDb = NULL; } } #endif ILibCriticalLogFilename = ILibString_Copy(MeshAgent_MakeAbsolutePath(agentHost->exePath, ".log"), -1); #ifndef MICROSTACK_NOTLS util_openssl_init(); #endif ILibChain_OnDestroyEvent_AddHandler(agentHost->chain, MeshAgent_ChainEnd, agentHost); #ifdef WIN32 x = ILibString_LastIndexOf(param[0], -1, "\\", 1); if (x > 0) { strncpy_s(ILibScratchPad2, sizeof(ILibScratchPad2), param[0], x); ILibScratchPad2[x] = 0; SetCurrentDirectory(ILibScratchPad2); } #endif void *reserved[] = { agentHost, ¶mLen, param }; // Check to see if we are running as just a JavaScript Engine if (paramLen >= 2 && ILibString_EndsWith(param[1], -1, ".js", 3) != 0) { // We are acting as a scripting engine ILibChain_RunOnMicrostackThreadEx(agentHost->chain, MeshAgent_ScriptMode_Dispatched, reserved); ILibStartChain(agentHost->chain); agentHost->chain = NULL; } else { // We are acting as an Agent ILibChain_RunOnMicrostackThreadEx(agentHost->chain, MeshAgent_AgentMode_Dispatched, reserved); ILibStartChain(agentHost->chain); agentHost->chain = NULL; // Mesh agent has exited, set the chain to NULL // Check if we need to perform self-update if (agentHost->performSelfUpdate == 1) { int i, ptr = 0; #ifdef WIN32 STARTUPINFOA info = { sizeof(info) }; PROCESS_INFORMATION processInfo; #endif // Get the update executable path #ifdef WIN32 char* updateFilePath = MeshAgent_MakeAbsolutePath(agentHost->exePath, ".update.exe"); // uses ILibScratchPad2 #else char* updateFilePath = MeshAgent_MakeAbsolutePath(agentHost->exePath, ".update"); // uses ILibScratchPad2 #endif char str[4096]; // Kill the KVM slave process if needed #if defined(__APPLE__) && defined(_DAEMON) && defined(_LINKVM) if (kvm_clientProcessId != 0) kill(kvm_clientProcessId, SIGTERM); #endif // Build the argument list str[0] = 0; for (i = 1; i < paramLen; i++) ptr += sprintf_s(str + ptr, 4096 - ptr, " %s", param[i]); #ifdef WIN32 // Windows version sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "%s -update:\"%s\"%s", updateFilePath, agentHost->exePath, str); if (!CreateProcessA(NULL, ILibScratchPad, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo)) { // We tried to execute a bad executable... not good. Lets try to recover. if (updateFilePath != NULL && agentHost->exePath != NULL) { while (util_CopyFile(agentHost->exePath, updateFilePath, FALSE) == FALSE) Sleep(5000); if (CreateProcessA(NULL, ILibScratchPad, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo)) { CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } } } else { CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } #else // Linux version { struct stat results; stat(agentHost->exePath, &results); // This the mode of the current executable chmod(updateFilePath, results.st_mode); // Set the new executable to the same mode as the current one. sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "%s -update:\"%s\"%s &", updateFilePath, agentHost->exePath, str); // Launch the new executable for update. if (system(ILibScratchPad)) {} } #endif } } #ifndef MICROSTACK_NOTLS util_openssl_uninit(); #endif if (ILibCriticalLogFilename != NULL) free(ILibCriticalLogFilename); return 0; } void MeshAgent_Destroy(MeshAgentHostContainer* agent) { #ifndef MICROSTACK_NOTLS util_freecert(&agent->selftlscert); util_freecert(&agent->selfcert); util_freecert(&agent->selftlsclientcert); #endif if (agent->masterDb != NULL) { ILibSimpleDataStore_Close(agent->masterDb); } if (agent->chain != NULL) { ILibChain_DestroyEx(agent->chain); } free(agent); } void MeshAgent_Stop(MeshAgentHostContainer *agent) { ILibStopChain(agent->chain); } #ifdef WIN32 // Perform self-update (Windows console/tray version) void MeshAgent_PerformSelfUpdate(char* selfpath, char* exepath, int argc, char **argv) { int i, ptr = 0; STARTUPINFOA info = { sizeof(info) }; PROCESS_INFORMATION processInfo; // Sleep for 5 seconds, this will give some time for the calling process to get going. Sleep(5000); // Built the argument list ILibScratchPad[0] = 0; for (i = 2; i < argc; i++) ptr += sprintf_s(ILibScratchPad + ptr, 4096 - ptr, " %s", argv[i]); sprintf_s(ILibScratchPad2, 60000, "%s%s", exepath, ILibScratchPad); // Attempt to copy our own exe over the original exe while (util_CopyFile(selfpath, exepath, FALSE) == FALSE) Sleep(5000); // Now run the process if (!CreateProcessA(NULL, ILibScratchPad2, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo)) { // TODO: Failed to run update. } else { CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } } /* #elif defined(__APPLE__) && defined(_DAEMON) // Perform self-update (Apple Daemon) void MeshAgent_PerformSelfUpdate(char* selfpath, char* exepath, int argc, char **argv) { int i; // First, we wait a little to give time for the calling process to exit sleep(5); // Attempt to copy our own exe over the remove(exepath); snprintf(ILibScratchPad2, 6000, "cp %s %s", selfpath, exepath); while (system(ILibScratchPad2) != 0) { sleep(5); remove(exepath); } // Now run the updated process i = system("/sbin/SystemStarter restart \"MeshAgent\""); UNREFERENCED_PARAMETER(i); } */ #elif ANDROID // no self update #elif NACL // no self update #else // Perform self-update (Linux version) void MeshAgent_PerformSelfUpdate(char* selfpath, char* exepath, int argc, char **argv) { int i, ptr = 0; // First, we wait a little to give time for the calling process to exit sleep(5); // Attempt to copy our own exe over remove(exepath); sprintf_s(ILibScratchPad2, 6000, "cp %s %s", selfpath, exepath); while (system(ILibScratchPad2) != 0) { sleep(5); remove(exepath); } // Built the argument list ILibScratchPad[0] = 0; for (i = 2; i < argc; i++) ptr += sprintf_s(ILibScratchPad + ptr, 4096 - ptr, " %s", argv[i]); sprintf_s(ILibScratchPad2, 60000, "%s%s &", exepath, ILibScratchPad); // Now run the updated process i = system(ILibScratchPad2); UNREFERENCED_PARAMETER(i); } #endif #ifdef __DOXY__ /*! \implements EventEmitter \brief JavaScript object interface for native Mesh Agent */ class MeshAgent { public: /*! \brief Event emitted when the MeshAgent is initialized and ready to connect */ void Ready; /*! \brief Event emitted when the MeshAgent has established an authenticated control channel with the server */ void Connected; void kvmConnected; /*! \brief Event emitted when an LMS notification is received \param notification Notification object in the format:\n messageId \ indicationTime \ messageArguments \ rawXML \ */ void lmsNotification; /*! \brief The Mesh Server the agent is connected to. If LAN Mode, will be 'local' */ String ConnectedServer; /*! \brief The fully qualified network uri that the agent is connected to (ie: wss://www.meshcentral.com:443/agent.ashx) */ String ServerUrl; /*! \brief Returns a DuplexStream KVM session, that can be piped to a data channel. */ DuplexStream getRemoteDesktopStream(); /*! \brief Add a function callback to be added to the list of Command listeners, which gets dispatched whenever a command is received from the server. \param handler */ void AddCommandHandler(handler); /*! \brief Add a function callback that will get dispatched when the MeshAgent successfully establishes an authenticated control channel with the server \param handler */ void AddConnectHandler(handler); /*! \brief Send a command to the server \param cmd \ Command to send \return False if the calling code should wait for the 'drain' event before sending more commands */ bool SendCommand(cmd); /*! \brief Property indicating if MicroLMS is active */ Integer activeMicroLMS; /*! \brief Property indicating if KVM Support is present */ Integer hasKVM; /*! \brief Property indicating if HECI support is present */ Integer hasHECI; /*! \brief Property containing MEInfo */ Object MEInfo; /*! \brief Property containing NetInfo */ Object NetInfo; /*! \brief Executes power state command on the computer (ie: Sleep, Hibernate...) \param powerState \param force Optional \return status */ Integer ExecPowerState(powerState[, force]); /*! \brief Generates a new self signed certificate \param passphrase \ passphrase for private key \return \ PKS encoded certificate */ Buffer GenerateCertificate(passphrase); }; #endif