From 9adec0ffda0a1e60f3b834e52099c6a2094c331e Mon Sep 17 00:00:00 2001 From: Mattias Gustavsson Date: Wed, 29 Sep 2021 15:47:57 +0200 Subject: [PATCH] Calling auto update from demo app --- auto_update/auto_update_helper.c | 40 +++++---- auto_update/auto_update_service.c | 139 ++++++++++++++++-------------- auto_update/manifest.xml | 10 +++ auto_update/package.json | 2 +- src/app/auto-update-handler.ts | 69 +++++++++++++++ src/app/main-api-handler.ts | 4 + src/common/api-interface.ts | 2 + src/demo/index.html | 20 +++++ src/renderer/app-bridge.ts | 3 + src/renderer/ssf-api.ts | 10 +++ 10 files changed, 219 insertions(+), 80 deletions(-) create mode 100644 auto_update/manifest.xml create mode 100644 src/app/auto-update-handler.ts diff --git a/auto_update/auto_update_helper.c b/auto_update/auto_update_helper.c index 9656b993..d037213e 100644 --- a/auto_update/auto_update_helper.c +++ b/auto_update/auto_update_helper.c @@ -35,23 +35,23 @@ void internal_log( char const* file, int line, char const* func, char const* lev } time_t rawtime; - struct tm* info; - time( &rawtime ); - info = localtime( &rawtime ); - int offset = g_log.time_offset; - int offs_s = offset % 60; - offset -= offs_s; - int offs_m = ( offset % (60 * 60) ) / 60; - offset -= offs_m * 60; - int offs_h = offset / ( 60 * 60 ); + struct tm* info; + time( &rawtime ); + info = localtime( &rawtime ); + int offset = g_log.time_offset; + int offs_s = offset % 60; + offset -= offs_s; + int offs_m = ( offset % (60 * 60) ) / 60; + offset -= offs_m * 60; + int offs_h = offset / ( 60 * 60 ); - fprintf( g_log.file, "%d-%02d-%02d %02d:%02d:%02d:025 %+02d:%02d | %s | %s(%d) | %s: ", info->tm_year + 1900, info->tm_mon + 1, - info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, offs_h, offs_m, level, file, line, func ); - va_list args; - va_start( args, format ); - vfprintf( g_log.file, format, args ); - va_end( args ); - fflush( g_log.file ); + fprintf( g_log.file, "%d-%02d-%02d %02d:%02d:%02d:025 %+02d:%02d | %s | %s(%d) | %s: ", info->tm_year + 1900, info->tm_mon + 1, + info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, offs_h, offs_m, level, file, line, func ); + va_list args; + va_start( args, format ); + vfprintf( g_log.file, format, args ); + va_end( args ); + fflush( g_log.file ); } LeaveCriticalSection( &g_log.mutex ); @@ -146,6 +146,10 @@ int main( int argc, char** argv ) { while( size < sizeof( response ) - 1 && status == IPC_RECEIVE_STATUS_MORE_DATA ) { status = ipc_client_receive( client, response + size, sizeof( response ) - size - 1, &temp_size ); + if( status == IPC_RECEIVE_STATUS_ERROR ) { + LOG_ERROR( "Receiving response failed" ); + break; + } size += temp_size; } response[ size ] = '\0'; @@ -175,6 +179,10 @@ int main( int argc, char** argv ) { while( size < sizeof( response ) - 1 && status == IPC_RECEIVE_STATUS_MORE_DATA ) { status = ipc_client_receive( client, response + size, sizeof( response ) - size - 1, &temp_size ); + if( status == IPC_RECEIVE_STATUS_ERROR ) { + LOG_ERROR( "Receiving response failed" ); + break; + } size += temp_size; } response[ size ] = '\0'; diff --git a/auto_update/auto_update_service.c b/auto_update/auto_update_service.c index 124be59c..50f37452 100644 --- a/auto_update/auto_update_service.c +++ b/auto_update/auto_update_service.c @@ -35,6 +35,9 @@ // or the installation packages signed with it will be rejected. struct { BYTE hash[ 20 ]; } thumbprints[] = { + /* e846d3fb2a93007e921c3affcd7032f0186f116a */ + "\xe8\x46\xd3\xfb\x2a\x93\x00\x7e\x92\x1c\x3a\xff\xcd\x70\x32\xf0\x18\x6f\x11\x6a", + /* 99b3333ac4457a4e21a527cc11040b28c15c1d3f */ "\x99\xb3\x33\x3a\xc4\x45\x7a\x4e\x21\xa5\x27\xcc\x11\x04\x0b\x28\xc1\x5c\x1d\x3f", }; @@ -68,36 +71,36 @@ void internal_log( char const* file, int line, char const* func, char const* lev } time_t rawtime; - struct tm* info; - time( &rawtime ); - info = localtime( &rawtime ); - int offset = g_log.time_offset; - int offs_s = offset % 60; - offset -= offs_s; - int offs_m = ( offset % (60 * 60) ) / 60; - offset -= offs_m * 60; - int offs_h = offset / ( 60 * 60 ); + struct tm* info; + time( &rawtime ); + info = localtime( &rawtime ); + int offset = g_log.time_offset; + int offs_s = offset % 60; + offset -= offs_s; + int offs_m = ( offset % (60 * 60) ) / 60; + offset -= offs_m * 60; + int offs_h = offset / ( 60 * 60 ); if( g_log.file ) { - fprintf( g_log.file, "%d-%02d-%02d %02d:%02d:%02d:025 %+02d:%02d | %s | %s(%d) | %s: ", info->tm_year + 1900, info->tm_mon + 1, - info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, offs_h, offs_m, level, file, line, func ); - va_list args; - va_start( args, format ); - vfprintf( g_log.file, format, args ); - va_end( args ); - fflush( g_log.file ); + fprintf( g_log.file, "%d-%02d-%02d %02d:%02d:%02d:025 %+02d:%02d | %s | %s(%d) | %s: ", info->tm_year + 1900, info->tm_mon + 1, + info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, offs_h, offs_m, level, file, line, func ); + va_list args; + va_start( args, format ); + vfprintf( g_log.file, format, args ); + va_end( args ); + fflush( g_log.file ); } size_t len = IPC_MESSAGE_MAX_LENGTH; char* buffer = (char*) malloc( len + 1 ); - size_t count = snprintf( buffer, len, "%d-%02d-%02d %02d:%02d:%02d:025 %+02d:%02d | %s | %s(%d) | %s: ", info->tm_year + 1900, info->tm_mon + 1, - info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, offs_h, offs_m, level, file, line, func ); + size_t count = snprintf( buffer, len, "%d-%02d-%02d %02d:%02d:%02d:025 %+02d:%02d | %s | %s(%d) | %s: ", info->tm_year + 1900, info->tm_mon + 1, + info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, offs_h, offs_m, level, file, line, func ); buffer[ count ] = '\0'; - va_list args; - va_start( args, format ); - count += vsnprintf( buffer + count, len - count, format, args ); + va_list args; + va_start( args, format ); + count += vsnprintf( buffer + count, len - count, format, args ); buffer[ count ] = '\0'; - va_end( args ); + va_end( args ); if( g_log.count >= g_log.capacity ) { g_log.capacity *= 2; @@ -260,7 +263,7 @@ BOOL VerifyEmbeddedSignature( LPCWSTR pwszSourceFile ) { // and Wintrust_Data. lStatus = WinVerifyTrust( NULL, &WVTPolicyGUID, &WinTrustData ); - BOOL result = FALSE; + BOOL result = FALSE; switch( lStatus ) { case ERROR_SUCCESS: /* @@ -277,7 +280,7 @@ BOOL VerifyEmbeddedSignature( LPCWSTR pwszSourceFile ) { subject. */ LOG_INFO( "The file is signed and the signature was verified." ); - result = TRUE; + result = TRUE; break; case TRUST_E_NOSIGNATURE: @@ -348,54 +351,57 @@ BOOL validate_installer( char const* filename ) { wchar_t* wfilename = (wchar_t*) malloc( sizeof( wchar_t* ) * ( length + 1 ) ); mbstowcs_s( &length, wfilename, length, filename, strlen( filename ) ); BOOL signature_valid = VerifyEmbeddedSignature( wfilename ); - if( !signature_valid ) { - free( wfilename ); - return FALSE; - } + if( !signature_valid ) { + LOG_ERROR( "The installer was not signed with a valid certificate" ); + free( wfilename ); + return FALSE; + } DWORD dwEncoding, dwContentType, dwFormatType; HCERTSTORE hStore = NULL; HCRYPTMSG hMsg = NULL; - BOOL result = CryptQueryObject( CERT_QUERY_OBJECT_FILE, - wfilename, - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, - CERT_QUERY_FORMAT_FLAG_BINARY, - 0, - &dwEncoding, - &dwContentType, - &dwFormatType, - &hStore, - &hMsg, - NULL ); + BOOL result = CryptQueryObject( CERT_QUERY_OBJECT_FILE, + wfilename, + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, + CERT_QUERY_FORMAT_FLAG_BINARY, + 0, + &dwEncoding, + &dwContentType, + &dwFormatType, + &hStore, + &hMsg, + NULL ); free( wfilename ); - + if( !result ) { - LOG_LAST_ERROR( "CryptQueryObject failed" ); - return FALSE; - } + LOG_LAST_ERROR( "CryptQueryObject failed" ); + return FALSE; + } - BOOL found = FALSE; - for( int i = 0; i < sizeof( thumbprints ) / sizeof( *thumbprints ); ++i ) { - CRYPT_HASH_BLOB hash_blob = { + BOOL found = FALSE; + for( int i = 0; i < sizeof( thumbprints ) / sizeof( *thumbprints ); ++i ) { + CRYPT_HASH_BLOB hash_blob = { sizeof( thumbprints[ i ].hash ) / sizeof( *(thumbprints[ i ].hash) ), thumbprints[ i ].hash }; - CERT_CONTEXT const* cert_context = CertFindCertificateInStore( - hStore, - (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), - 0, - CERT_FIND_HASH, - &hash_blob, - NULL ); - + CERT_CONTEXT const* cert_context = CertFindCertificateInStore( + hStore, + (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING), + 0, + CERT_FIND_HASH, + &hash_blob, + NULL ); + if( cert_context ) { - found = TRUE; - CertFreeCertificateContext( cert_context ); - } - } - - CryptMsgClose( hMsg ); - CertCloseStore( hStore, CERT_CLOSE_STORE_FORCE_FLAG ); + found = TRUE; + CertFreeCertificateContext( cert_context ); + } + } + if( !found ) { + LOG_ERROR( "The installer was not signed with a known Symphony certificate" ); + } + CryptMsgClose( hMsg ); + CertCloseStore( hStore, CERT_CLOSE_STORE_FORCE_FLAG ); return found; } @@ -407,9 +413,11 @@ bool run_installer( char const* filename ) { LOG_ERROR( "The signature of %s could is not a valid Symphony signature" ); return false; } + LOG_INFO( "Signature of installer successfully validated" ); - char command[ 512 ]; - sprintf( command, "/i %s /q", filename ); + char command[ 512 ]; + sprintf( command, "/i %s /q", filename ); + LOG_INFO( "MSI command: %s", command ); SHELLEXECUTEINFO ShExecInfo = { 0 }; ShExecInfo.cbSize = sizeof( SHELLEXECUTEINFO ); @@ -422,9 +430,12 @@ bool run_installer( char const* filename ) { ShExecInfo.nShow = SW_SHOW; ShExecInfo.hInstApp = NULL; if( ShellExecuteEx( &ShExecInfo ) ) { + LOG_INFO( "ShellExecuteEx successful, waiting to finish" ); WaitForSingleObject( ShExecInfo.hProcess, INFINITE ); + LOG_INFO( "ShellExecuteEx finished" ); DWORD exitCode = 0; GetExitCodeProcess( ShExecInfo.hProcess, &exitCode ); + LOG_INFO( "ShellExecuteEx exit code: %d", exitCode ); CloseHandle( ShExecInfo.hProcess ); return exitCode == 0 ? true : false; } else { @@ -451,8 +462,10 @@ void ipc_handler( char const* request, void* user_data, char* response, size_t c // "msi" - run installer LOG_INFO( "MSI command, running installer" ); if( run_installer( request + 4 ) ) { + LOG_INFO( "Response: OK" ); strcpy( response, "OK" ); } else { + LOG_INFO( "Response: ERROR" ); strcpy( response, "ERROR" ); } } else if( strlen( request ) == 3 && stricmp( request, "log" ) == 0 ) { diff --git a/auto_update/manifest.xml b/auto_update/manifest.xml new file mode 100644 index 00000000..d3f18451 --- /dev/null +++ b/auto_update/manifest.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/auto_update/package.json b/auto_update/package.json index c21114ba..eae7f2e7 100644 --- a/auto_update/package.json +++ b/auto_update/package.json @@ -5,6 +5,6 @@ "scripts": { "test": "cl tests.cpp /O2 /MTd /D_DEBUG /D_CRTDBG_MAP_ALLOC /nologo /link /SUBSYSTEM:CONSOLE && tests.exe", "preinstall": "npm run test && npm run build", - "build": "rc.exe /nologo auto_update_helper.rc & cl auto_update_helper.c auto_update_helper.res /O2 /MT /nologo /link /SUBSYSTEM:CONSOLE & rc.exe /nologo auto_update_service.rc & cl auto_update_service.c auto_update_service.res /O2 /MT /nologo /link /SUBSYSTEM:CONSOLE" + "build": "rc.exe /nologo auto_update_helper.rc & cl auto_update_helper.c auto_update_helper.res /O2 /MT /nologo /link /SUBSYSTEM:CONSOLE /MANIFESTINPUT:manifest.xml /MANIFEST:EMBED & rc.exe /nologo auto_update_service.rc & cl auto_update_service.c auto_update_service.res /O2 /MT /nologo /link /SUBSYSTEM:CONSOLE /MANIFESTINPUT:manifest.xml /MANIFEST:EMBED" } } diff --git a/src/app/auto-update-handler.ts b/src/app/auto-update-handler.ts new file mode 100644 index 00000000..076cf750 --- /dev/null +++ b/src/app/auto-update-handler.ts @@ -0,0 +1,69 @@ +import { app } from 'electron'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +import { spawn } from 'child_process'; +import { isDevEnv, isElectronQA, isWindowsOS } from '../common/env'; +import { logger } from '../common/logger'; + +class AutoUpdate { + private readonly tempDir: string; + private filename: string; + private logFilePath: string; + private updateUtil: string; + private updateUtilArgs: ReadonlyArray; + + constructor() { + if (isElectronQA) { + this.tempDir = os.tmpdir(); + } else { + this.tempDir = path.join(app.getPath('userData'), 'temp'); + if (!fs.existsSync(this.tempDir)) { + fs.mkdirSync(this.tempDir); + } + } + + this.filename = ''; + this.logFilePath = path.join(this.tempDir, 'auto_update.log'); + + this.updateUtil = isDevEnv + ? path.join( + __dirname, + '../../../node_modules/auto-update/auto_update_helper.exe', + ) + : path.join(path.dirname(app.getPath('exe')), 'auto_update_helper.exe'); + + this.updateUtilArgs = []; + } + + /** + * Launch the auto update helper + */ + public async update(filename: string) { + logger.info(`auto-update-handler: Starting auto update!`); + this.filename = filename; + if (isWindowsOS) { + this.updateUtilArgs = [ + this.filename, + app.getPath('exe'), + this.logFilePath, + ]; + logger.info( + `auto-update-handler: Running update helper${this.updateUtil} with args ${this.updateUtilArgs}!`, + ); + + try { + spawn(this.updateUtil, this.updateUtilArgs); + } catch (error) { + logger.error( + `auto-update-handler: auto update failed. Error: ${error}!`, + ); + } + } + } +} + +const autoUpdate = new AutoUpdate(); + +export { autoUpdate }; diff --git a/src/app/main-api-handler.ts b/src/app/main-api-handler.ts index fffbfb4c..ce3956ae 100644 --- a/src/app/main-api-handler.ts +++ b/src/app/main-api-handler.ts @@ -10,6 +10,7 @@ import { logger } from '../common/logger'; import { activityDetection } from './activity-detection'; import { analytics } from './analytics-handler'; import appStateHandler from './app-state-handler'; +import { autoUpdate } from './auto-update-handler'; import { CloudConfigDataTypes, config, ICloudConfig } from './config-handler'; import { downloadHandler } from './download-handler'; import { memoryMonitor } from './memory-monitor'; @@ -291,6 +292,9 @@ ipcMain.on( ?.webContents.setZoomFactor(arg.zoomLevel as number); } break; + case apiCmds.autoUpdate: + autoUpdate.update(arg.filename); + break; default: break; } diff --git a/src/common/api-interface.ts b/src/common/api-interface.ts index ededb13d..e7a15e87 100644 --- a/src/common/api-interface.ts +++ b/src/common/api-interface.ts @@ -47,6 +47,7 @@ export enum apiCmds { showNotification = 'show-notification', closeAllWrapperWindows = 'close-all-windows', setZoomLevel = 'set-zoom-level', + autoUpdate = 'auto-update', } export enum apiName { @@ -87,6 +88,7 @@ export interface IApiArgs { notificationId: number; theme: Themes; zoomLevel: number; + filename: string; } export type Themes = 'light' | 'dark'; diff --git a/src/demo/index.html b/src/demo/index.html index 2cc218be..4fbfee3c 100644 --- a/src/demo/index.html +++ b/src/demo/index.html @@ -270,6 +270,14 @@ + +
+

Auto-update Test

+ Full path to MSI +
+ +
+