diff options
author | Markus Mohrhard <markus.mohrhard@googlemail.com> | 2017-01-06 05:42:34 +0100 |
---|---|---|
committer | Markus Mohrhard <markus.mohrhard@googlemail.com> | 2017-05-19 03:43:28 +0200 |
commit | c3d264b52d2516e00f641ed2f7bb71f367a6400b (patch) | |
tree | 0330ade14a1884063ba86b776abca78848a515bb /onlineupdate/source/service | |
parent | e88f080bacabe9d7196813aa6d17132a389c330b (diff) |
use a more libreoffice like formatting
This formatting has been done by an astyle script. The idea is that with
a consistent formatting it is much easier to bring in changes from
Mozilla. Updating the code with the new version, running the astyle
script and then looking through the diff seems like the most sensible
option.
Diffstat (limited to 'onlineupdate/source/service')
-rw-r--r-- | onlineupdate/source/service/certificatecheck.cxx | 382 | ||||
-rw-r--r-- | onlineupdate/source/service/certificatecheck.hxx | 8 | ||||
-rw-r--r-- | onlineupdate/source/service/maintenanceservice.cxx | 517 | ||||
-rw-r--r-- | onlineupdate/source/service/maintenanceservice.hxx | 4 | ||||
-rw-r--r-- | onlineupdate/source/service/registrycertificates.cxx | 214 | ||||
-rw-r--r-- | onlineupdate/source/service/resource.hxx | 2 | ||||
-rw-r--r-- | onlineupdate/source/service/servicebase.cxx | 114 | ||||
-rw-r--r-- | onlineupdate/source/service/servicebase.hxx | 2 | ||||
-rw-r--r-- | onlineupdate/source/service/serviceinstall.cxx | 1232 | ||||
-rw-r--r-- | onlineupdate/source/service/serviceinstall.hxx | 4 | ||||
-rw-r--r-- | onlineupdate/source/service/workmonitor.cxx | 1062 |
11 files changed, 1896 insertions, 1645 deletions
diff --git a/onlineupdate/source/service/certificatecheck.cxx b/onlineupdate/source/service/certificatecheck.cxx index 3a9eba020ccb..58d70162f40c 100644 --- a/onlineupdate/source/service/certificatecheck.cxx +++ b/onlineupdate/source/service/certificatecheck.cxx @@ -28,87 +28,97 @@ DWORD CheckCertificateForPEFile(LPCWSTR filePath, CertificateCheckInfo &infoToMatch) { - HCERTSTORE certStore = nullptr; - HCRYPTMSG cryptMsg = nullptr; - PCCERT_CONTEXT certContext = nullptr; - PCMSG_SIGNER_INFO signerInfo = nullptr; - DWORD lastError = ERROR_SUCCESS; + HCERTSTORE certStore = nullptr; + HCRYPTMSG cryptMsg = nullptr; + PCCERT_CONTEXT certContext = nullptr; + PCMSG_SIGNER_INFO signerInfo = nullptr; + DWORD lastError = ERROR_SUCCESS; - // Get the HCERTSTORE and HCRYPTMSG from the signed file. - DWORD encoding, contentType, formatType; - BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, - filePath, - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, - CERT_QUERY_CONTENT_FLAG_ALL, - 0, &encoding, &contentType, - &formatType, &certStore, &cryptMsg, nullptr); - if (!result) { - lastError = GetLastError(); - LOG_WARN(("CryptQueryObject failed. (%d)", lastError)); - goto cleanup; - } + // Get the HCERTSTORE and HCRYPTMSG from the signed file. + DWORD encoding, contentType, formatType; + BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, + filePath, + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, + CERT_QUERY_CONTENT_FLAG_ALL, + 0, &encoding, &contentType, + &formatType, &certStore, &cryptMsg, nullptr); + if (!result) + { + lastError = GetLastError(); + LOG_WARN(("CryptQueryObject failed. (%d)", lastError)); + goto cleanup; + } - // Pass in nullptr to get the needed signer information size. - DWORD signerInfoSize; - result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, - nullptr, &signerInfoSize); - if (!result) { - lastError = GetLastError(); - LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); - goto cleanup; - } + // Pass in nullptr to get the needed signer information size. + DWORD signerInfoSize; + result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, + nullptr, &signerInfoSize); + if (!result) + { + lastError = GetLastError(); + LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); + goto cleanup; + } - // Allocate the needed size for the signer information. - signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize); - if (!signerInfo) { - lastError = GetLastError(); - LOG_WARN(("Unable to allocate memory for Signer Info. (%d)", lastError)); - goto cleanup; - } + // Allocate the needed size for the signer information. + signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize); + if (!signerInfo) + { + lastError = GetLastError(); + LOG_WARN(("Unable to allocate memory for Signer Info. (%d)", lastError)); + goto cleanup; + } - // Get the signer information (PCMSG_SIGNER_INFO). - // In particular we want the issuer and serial number. - result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, - (PVOID)signerInfo, &signerInfoSize); - if (!result) { - lastError = GetLastError(); - LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); - goto cleanup; - } + // Get the signer information (PCMSG_SIGNER_INFO). + // In particular we want the issuer and serial number. + result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, + (PVOID)signerInfo, &signerInfoSize); + if (!result) + { + lastError = GetLastError(); + LOG_WARN(("CryptMsgGetParam failed. (%d)", lastError)); + goto cleanup; + } - // Search for the signer certificate in the certificate store. - CERT_INFO certInfo; - certInfo.Issuer = signerInfo->Issuer; - certInfo.SerialNumber = signerInfo->SerialNumber; - certContext = CertFindCertificateInStore(certStore, ENCODING, 0, - CERT_FIND_SUBJECT_CERT, - (PVOID)&certInfo, nullptr); - if (!certContext) { - lastError = GetLastError(); - LOG_WARN(("CertFindCertificateInStore failed. (%d)", lastError)); - goto cleanup; - } + // Search for the signer certificate in the certificate store. + CERT_INFO certInfo; + certInfo.Issuer = signerInfo->Issuer; + certInfo.SerialNumber = signerInfo->SerialNumber; + certContext = CertFindCertificateInStore(certStore, ENCODING, 0, + CERT_FIND_SUBJECT_CERT, + (PVOID)&certInfo, nullptr); + if (!certContext) + { + lastError = GetLastError(); + LOG_WARN(("CertFindCertificateInStore failed. (%d)", lastError)); + goto cleanup; + } - if (!DoCertificateAttributesMatch(certContext, infoToMatch)) { - lastError = ERROR_NOT_FOUND; - LOG_WARN(("Certificate did not match issuer or name. (%d)", lastError)); - goto cleanup; - } + if (!DoCertificateAttributesMatch(certContext, infoToMatch)) + { + lastError = ERROR_NOT_FOUND; + LOG_WARN(("Certificate did not match issuer or name. (%d)", lastError)); + goto cleanup; + } cleanup: - if (signerInfo) { - LocalFree(signerInfo); - } - if (certContext) { - CertFreeCertificateContext(certContext); - } - if (certStore) { - CertCloseStore(certStore, 0); - } - if (cryptMsg) { - CryptMsgClose(cryptMsg); - } - return lastError; + if (signerInfo) + { + LocalFree(signerInfo); + } + if (certContext) + { + CertFreeCertificateContext(certContext); + } + if (certStore) + { + CertCloseStore(certStore, 0); + } + if (cryptMsg) + { + CryptMsgClose(cryptMsg); + } + return lastError; } /** @@ -122,86 +132,96 @@ BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, CertificateCheckInfo &infoToMatch) { - DWORD dwData; - LPTSTR szName = nullptr; + DWORD dwData; + LPTSTR szName = nullptr; - if (infoToMatch.issuer) { - // Pass in nullptr to get the needed size of the issuer buffer. - dwData = CertGetNameString(certContext, - CERT_NAME_SIMPLE_DISPLAY_TYPE, - CERT_NAME_ISSUER_FLAG, nullptr, - nullptr, 0); + if (infoToMatch.issuer) + { + // Pass in nullptr to get the needed size of the issuer buffer. + dwData = CertGetNameString(certContext, + CERT_NAME_SIMPLE_DISPLAY_TYPE, + CERT_NAME_ISSUER_FLAG, nullptr, + nullptr, 0); - if (!dwData) { - LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); - return FALSE; - } + if (!dwData) + { + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); + return FALSE; + } - // Allocate memory for Issuer name buffer. - LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); - if (!szName) { - LOG_WARN(("Unable to allocate memory for issuer name. (%d)", - GetLastError())); - return FALSE; - } + // Allocate memory for Issuer name buffer. + LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); + if (!szName) + { + LOG_WARN(("Unable to allocate memory for issuer name. (%d)", + GetLastError())); + return FALSE; + } - // Get Issuer name. - if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, - CERT_NAME_ISSUER_FLAG, nullptr, szName, dwData)) { - LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); - LocalFree(szName); - return FALSE; - } + // Get Issuer name. + if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, + CERT_NAME_ISSUER_FLAG, nullptr, szName, dwData)) + { + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); + LocalFree(szName); + return FALSE; + } - // If the issuer does not match, return a failure. - if (!infoToMatch.issuer || - wcscmp(szName, infoToMatch.issuer)) { - LocalFree(szName); - return FALSE; + // If the issuer does not match, return a failure. + if (!infoToMatch.issuer || + wcscmp(szName, infoToMatch.issuer)) + { + LocalFree(szName); + return FALSE; + } + + LocalFree(szName); + szName = nullptr; } - LocalFree(szName); - szName = nullptr; - } + if (infoToMatch.name) + { + // Pass in nullptr to get the needed size of the name buffer. + dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, + 0, nullptr, nullptr, 0); + if (!dwData) + { + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); + return FALSE; + } - if (infoToMatch.name) { - // Pass in nullptr to get the needed size of the name buffer. - dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, - 0, nullptr, nullptr, 0); - if (!dwData) { - LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); - return FALSE; - } + // Allocate memory for the name buffer. + szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); + if (!szName) + { + LOG_WARN(("Unable to allocate memory for subject name. (%d)", + GetLastError())); + return FALSE; + } - // Allocate memory for the name buffer. - szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR)); - if (!szName) { - LOG_WARN(("Unable to allocate memory for subject name. (%d)", - GetLastError())); - return FALSE; - } + // Obtain the name. + if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + nullptr, szName, dwData))) + { + LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); + LocalFree(szName); + return FALSE; + } - // Obtain the name. - if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - nullptr, szName, dwData))) { - LOG_WARN(("CertGetNameString failed. (%d)", GetLastError())); - LocalFree(szName); - return FALSE; - } + // If the issuer does not match, return a failure. + if (!infoToMatch.name || + wcscmp(szName, infoToMatch.name)) + { + LocalFree(szName); + return FALSE; + } - // If the issuer does not match, return a failure. - if (!infoToMatch.name || - wcscmp(szName, infoToMatch.name)) { - LocalFree(szName); - return FALSE; + // We have a match! + LocalFree(szName); } - // We have a match! - LocalFree(szName); - } - - // If there were any errors we would have aborted by now. - return TRUE; + // If there were any errors we would have aborted by now. + return TRUE; } /** @@ -213,12 +233,13 @@ DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, LPWSTR AllocateAndCopyWideString(LPCWSTR inputString) { - LPWSTR outputString = - (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR)); - if (outputString) { - lstrcpyW(outputString, inputString); - } - return outputString; + LPWSTR outputString = + (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR)); + if (outputString) + { + lstrcpyW(outputString, inputString); + } + return outputString; } /** @@ -230,41 +251,42 @@ AllocateAndCopyWideString(LPCWSTR inputString) DWORD VerifyCertificateTrustForFile(LPCWSTR filePath) { - // Setup the file to check. - WINTRUST_FILE_INFO fileToCheck; - ZeroMemory(&fileToCheck, sizeof(fileToCheck)); - fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); - fileToCheck.pcwszFilePath = filePath; + // Setup the file to check. + WINTRUST_FILE_INFO fileToCheck; + ZeroMemory(&fileToCheck, sizeof(fileToCheck)); + fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); + fileToCheck.pcwszFilePath = filePath; - // Setup what to check, we want to check it is signed and trusted. - WINTRUST_DATA trustData; - ZeroMemory(&trustData, sizeof(trustData)); - trustData.cbStruct = sizeof(trustData); - trustData.pPolicyCallbackData = nullptr; - trustData.pSIPClientData = nullptr; - trustData.dwUIChoice = WTD_UI_NONE; - trustData.fdwRevocationChecks = WTD_REVOKE_NONE; - trustData.dwUnionChoice = WTD_CHOICE_FILE; - trustData.dwStateAction = 0; - trustData.hWVTStateData = nullptr; - trustData.pwszURLReference = nullptr; - // no UI - trustData.dwUIContext = 0; - trustData.pFile = &fileToCheck; + // Setup what to check, we want to check it is signed and trusted. + WINTRUST_DATA trustData; + ZeroMemory(&trustData, sizeof(trustData)); + trustData.cbStruct = sizeof(trustData); + trustData.pPolicyCallbackData = nullptr; + trustData.pSIPClientData = nullptr; + trustData.dwUIChoice = WTD_UI_NONE; + trustData.fdwRevocationChecks = WTD_REVOKE_NONE; + trustData.dwUnionChoice = WTD_CHOICE_FILE; + trustData.dwStateAction = 0; + trustData.hWVTStateData = nullptr; + trustData.pwszURLReference = nullptr; + // no UI + trustData.dwUIContext = 0; + trustData.pFile = &fileToCheck; - GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; - // Check if the file is signed by something that is trusted. - LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData); - if (ERROR_SUCCESS == ret) { - // The hash that represents the subject is trusted and there were no - // verification errors. No publisher nor time stamp chain errors. - LOG(("The file \"%ls\" is signed and the signature was verified.", - filePath)); - return ERROR_SUCCESS; - } + GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; + // Check if the file is signed by something that is trusted. + LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData); + if (ERROR_SUCCESS == ret) + { + // The hash that represents the subject is trusted and there were no + // verification errors. No publisher nor time stamp chain errors. + LOG(("The file \"%ls\" is signed and the signature was verified.", + filePath)); + return ERROR_SUCCESS; + } - DWORD lastError = GetLastError(); - LOG_WARN(("There was an error validating trust of the certificate for file" - " \"%ls\". Returned: %d. (%d)", filePath, ret, lastError)); - return ret; + DWORD lastError = GetLastError(); + LOG_WARN(("There was an error validating trust of the certificate for file" + " \"%ls\". Returned: %d. (%d)", filePath, ret, lastError)); + return ret; } diff --git a/onlineupdate/source/service/certificatecheck.hxx b/onlineupdate/source/service/certificatecheck.hxx index 43a7c85b6b77..1efbcb153a65 100644 --- a/onlineupdate/source/service/certificatecheck.hxx +++ b/onlineupdate/source/service/certificatecheck.hxx @@ -9,14 +9,14 @@ struct CertificateCheckInfo { - LPCWSTR name; - LPCWSTR issuer; + LPCWSTR name; + LPCWSTR issuer; }; -BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT pCertContext, +BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT pCertContext, CertificateCheckInfo &infoToMatch); DWORD VerifyCertificateTrustForFile(LPCWSTR filePath); -DWORD CheckCertificateForPEFile(LPCWSTR filePath, +DWORD CheckCertificateForPEFile(LPCWSTR filePath, CertificateCheckInfo &infoToMatch); #endif diff --git a/onlineupdate/source/service/maintenanceservice.cxx b/onlineupdate/source/service/maintenanceservice.cxx index 8471bdbd56c5..036bfe25192f 100644 --- a/onlineupdate/source/service/maintenanceservice.cxx +++ b/onlineupdate/source/service/maintenanceservice.cxx @@ -33,92 +33,106 @@ BOOL GetLogDirectoryPath(WCHAR *path); int wmain(int argc, WCHAR **argv) { - if (argc < 2) - { - LOG_WARN(("missing mandatory command line argument")); - return 1; - } - // If command-line parameter is "install", install the service - // or upgrade if already installed - // If command line parameter is "forceinstall", install the service - // even if it is older than what is already installed. - // If command-line parameter is "upgrade", upgrade the service - // but do not install it if it is not already installed. - // If command line parameter is "uninstall", uninstall the service. - // Otherwise, the service is probably being started by the SCM. - bool forceInstall = !lstrcmpi(argv[1], L"forceinstall"); - if (!lstrcmpi(argv[1], L"install") || forceInstall) { - WCHAR updatePath[MAX_PATH + 1]; - if (GetLogDirectoryPath(updatePath)) { - LogInit(updatePath, L"maintenanceservice-install.log"); + if (argc < 2) + { + LOG_WARN(("missing mandatory command line argument")); + return 1; } - - SvcInstallAction action = InstallSvc; - if (forceInstall) { - action = ForceInstallSvc; - LOG(("Installing service with force specified...")); - } else { - LOG(("Installing service...")); + // If command-line parameter is "install", install the service + // or upgrade if already installed + // If command line parameter is "forceinstall", install the service + // even if it is older than what is already installed. + // If command-line parameter is "upgrade", upgrade the service + // but do not install it if it is not already installed. + // If command line parameter is "uninstall", uninstall the service. + // Otherwise, the service is probably being started by the SCM. + bool forceInstall = !lstrcmpi(argv[1], L"forceinstall"); + if (!lstrcmpi(argv[1], L"install") || forceInstall) + { + WCHAR updatePath[MAX_PATH + 1]; + if (GetLogDirectoryPath(updatePath)) + { + LogInit(updatePath, L"maintenanceservice-install.log"); + } + + SvcInstallAction action = InstallSvc; + if (forceInstall) + { + action = ForceInstallSvc; + LOG(("Installing service with force specified...")); + } + else + { + LOG(("Installing service...")); + } + + bool ret = SvcInstall(action); + if (!ret) + { + LOG_WARN(("Could not install service. (%d)", GetLastError())); + LogFinish(); + return 1; + } + + LOG(("The service was installed successfully")); + LogFinish(); + return 0; } - bool ret = SvcInstall(action); - if (!ret) { - LOG_WARN(("Could not install service. (%d)", GetLastError())); - LogFinish(); - return 1; + if (!lstrcmpi(argv[1], L"upgrade")) + { + WCHAR updatePath[MAX_PATH + 1]; + if (GetLogDirectoryPath(updatePath)) + { + LogInit(updatePath, L"maintenanceservice-install.log"); + } + + LOG(("Upgrading service if installed...")); + if (!SvcInstall(UpgradeSvc)) + { + LOG_WARN(("Could not upgrade service. (%d)", GetLastError())); + LogFinish(); + return 1; + } + + LOG(("The service was upgraded successfully")); + LogFinish(); + return 0; } - LOG(("The service was installed successfully")); - LogFinish(); - return 0; - } - - if (!lstrcmpi(argv[1], L"upgrade")) { - WCHAR updatePath[MAX_PATH + 1]; - if (GetLogDirectoryPath(updatePath)) { - LogInit(updatePath, L"maintenanceservice-install.log"); + if (!lstrcmpi(argv[1], L"uninstall")) + { + WCHAR updatePath[MAX_PATH + 1]; + if (GetLogDirectoryPath(updatePath)) + { + LogInit(updatePath, L"maintenanceservice-uninstall.log"); + } + LOG(("Uninstalling service...")); + if (!SvcUninstall()) + { + LOG_WARN(("Could not uninstall service. (%d)", GetLastError())); + LogFinish(); + return 1; + } + LOG(("The service was uninstalled successfully")); + LogFinish(); + return 0; } - LOG(("Upgrading service if installed...")); - if (!SvcInstall(UpgradeSvc)) { - LOG_WARN(("Could not upgrade service. (%d)", GetLastError())); - LogFinish(); - return 1; + SERVICE_TABLE_ENTRYW DispatchTable[] = + { + { SVC_NAME, (LPSERVICE_MAIN_FUNCTIONW) SvcMain }, + { nullptr, nullptr } + }; + + // This call returns when the service has stopped. + // The process should simply terminate when the call returns. + if (!StartServiceCtrlDispatcherW(DispatchTable)) + { + LOG_WARN(("StartServiceCtrlDispatcher failed. (%d)", GetLastError())); } - LOG(("The service was upgraded successfully")); - LogFinish(); return 0; - } - - if (!lstrcmpi(argv[1], L"uninstall")) { - WCHAR updatePath[MAX_PATH + 1]; - if (GetLogDirectoryPath(updatePath)) { - LogInit(updatePath, L"maintenanceservice-uninstall.log"); - } - LOG(("Uninstalling service...")); - if (!SvcUninstall()) { - LOG_WARN(("Could not uninstall service. (%d)", GetLastError())); - LogFinish(); - return 1; - } - LOG(("The service was uninstalled successfully")); - LogFinish(); - return 0; - } - - SERVICE_TABLE_ENTRYW DispatchTable[] = { - { SVC_NAME, (LPSERVICE_MAIN_FUNCTIONW) SvcMain }, - { nullptr, nullptr } - }; - - // This call returns when the service has stopped. - // The process should simply terminate when the call returns. - if (!StartServiceCtrlDispatcherW(DispatchTable)) { - LOG_WARN(("StartServiceCtrlDispatcher failed. (%d)", GetLastError())); - } - - return 0; } /** @@ -130,24 +144,27 @@ wmain(int argc, WCHAR **argv) BOOL GetLogDirectoryPath(WCHAR *path) { - HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA, nullptr, - SHGFP_TYPE_CURRENT, path); - if (FAILED(hr)) { - return FALSE; - } - - if (!PathAppendSafe(path, L"Mozilla")) { - return FALSE; - } - // The directory should already be created from the installer, but - // just to be safe in case someone deletes. - CreateDirectoryW(path, nullptr); - - if (!PathAppendSafe(path, L"logs")) { - return FALSE; - } - CreateDirectoryW(path, nullptr); - return TRUE; + HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA, nullptr, + SHGFP_TYPE_CURRENT, path); + if (FAILED(hr)) + { + return FALSE; + } + + if (!PathAppendSafe(path, L"Mozilla")) + { + return FALSE; + } + // The directory should already be created from the installer, but + // just to be safe in case someone deletes. + CreateDirectoryW(path, nullptr); + + if (!PathAppendSafe(path, L"logs")) + { + return FALSE; + } + CreateDirectoryW(path, nullptr); + return TRUE; } /** @@ -161,16 +178,19 @@ GetLogDirectoryPath(WCHAR *path) BOOL GetBackupLogPath(LPWSTR path, LPCWSTR basePath, int logNumber) { - WCHAR logName[64] = { L'\0' }; - wcsncpy(path, basePath, sizeof(logName) / sizeof(logName[0]) - 1); - if (logNumber <= 0) { - swprintf(logName, sizeof(logName) / sizeof(logName[0]), - L"maintenanceservice.log"); - } else { - swprintf(logName, sizeof(logName) / sizeof(logName[0]), - L"maintenanceservice-%d.log", logNumber); - } - return PathAppendSafe(path, logName); + WCHAR logName[64] = { L'\0' }; + wcsncpy(path, basePath, sizeof(logName) / sizeof(logName[0]) - 1); + if (logNumber <= 0) + { + swprintf(logName, sizeof(logName) / sizeof(logName[0]), + L"maintenanceservice.log"); + } + else + { + swprintf(logName, sizeof(logName) / sizeof(logName[0]), + L"maintenanceservice-%d.log", logNumber); + } + return PathAppendSafe(path, logName); } /** @@ -187,21 +207,25 @@ GetBackupLogPath(LPWSTR path, LPCWSTR basePath, int logNumber) void BackupOldLogs(LPCWSTR basePath, int numLogsToKeep) { - WCHAR oldPath[MAX_PATH + 1]; - WCHAR newPath[MAX_PATH + 1]; - for (int i = numLogsToKeep; i >= 1; i--) { - if (!GetBackupLogPath(oldPath, basePath, i -1)) { - continue; - } - - if (!GetBackupLogPath(newPath, basePath, i)) { - continue; + WCHAR oldPath[MAX_PATH + 1]; + WCHAR newPath[MAX_PATH + 1]; + for (int i = numLogsToKeep; i >= 1; i--) + { + if (!GetBackupLogPath(oldPath, basePath, i -1)) + { + continue; + } + + if (!GetBackupLogPath(newPath, basePath, i)) + { + continue; + } + + if (!MoveFileExW(oldPath, newPath, MOVEFILE_REPLACE_EXISTING)) + { + continue; + } } - - if (!MoveFileExW(oldPath, newPath, MOVEFILE_REPLACE_EXISTING)) { - continue; - } - } } /** @@ -221,20 +245,21 @@ BackupOldLogs(LPCWSTR basePath, int numLogsToKeep) DWORD WINAPI EnsureProcessTerminatedThread(LPVOID) { - Sleep(5000); - exit(0); + Sleep(5000); + exit(0); } void StartTerminationThread() { - // If the process does not self terminate like it should, this thread - // will terminate the process after 5 seconds. - HANDLE thread = CreateThread(nullptr, 0, EnsureProcessTerminatedThread, - nullptr, 0, nullptr); - if (thread) { - CloseHandle(thread); - } + // If the process does not self terminate like it should, this thread + // will terminate the process after 5 seconds. + HANDLE thread = CreateThread(nullptr, 0, EnsureProcessTerminatedThread, + nullptr, 0, nullptr); + if (thread) + { + CloseHandle(thread); + } } /** @@ -243,61 +268,65 @@ StartTerminationThread() void WINAPI SvcMain(DWORD argc, LPWSTR *argv) { - // Setup logging, and backup the old logs - WCHAR updatePath[MAX_PATH + 1]; - if (GetLogDirectoryPath(updatePath)) { - BackupOldLogs(updatePath, LOGS_TO_KEEP); - LogInit(updatePath, L"maintenanceservice.log"); - } - - // Disable every privilege we don't need. Processes started using - // CreateProcess will use the same token as this process. - UACHelper::DisablePrivileges(nullptr); - - // Register the handler function for the service - gSvcStatusHandle = RegisterServiceCtrlHandlerW(SVC_NAME, SvcCtrlHandler); - if (!gSvcStatusHandle) { - LOG_WARN(("RegisterServiceCtrlHandler failed. (%d)", GetLastError())); - ExecuteServiceCommand(argc, argv); - LogFinish(); - exit(1); - } - - // These values will be re-used later in calls involving gSvcStatus - gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - gSvcStatus.dwServiceSpecificExitCode = 0; + // Setup logging, and backup the old logs + WCHAR updatePath[MAX_PATH + 1]; + if (GetLogDirectoryPath(updatePath)) + { + BackupOldLogs(updatePath, LOGS_TO_KEEP); + LogInit(updatePath, L"maintenanceservice.log"); + } - // Report initial status to the SCM - ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); + // Disable every privilege we don't need. Processes started using + // CreateProcess will use the same token as this process. + UACHelper::DisablePrivileges(nullptr); + + // Register the handler function for the service + gSvcStatusHandle = RegisterServiceCtrlHandlerW(SVC_NAME, SvcCtrlHandler); + if (!gSvcStatusHandle) + { + LOG_WARN(("RegisterServiceCtrlHandler failed. (%d)", GetLastError())); + ExecuteServiceCommand(argc, argv); + LogFinish(); + exit(1); + } - // This event will be used to tell the SvcCtrlHandler when the work is - // done for when a stop comamnd is manually issued. - gWorkDoneEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - if (!gWorkDoneEvent) { - ReportSvcStatus(SERVICE_STOPPED, 1, 0); - StartTerminationThread(); - return; - } + // These values will be re-used later in calls involving gSvcStatus + gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + gSvcStatus.dwServiceSpecificExitCode = 0; + + // Report initial status to the SCM + ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); + + // This event will be used to tell the SvcCtrlHandler when the work is + // done for when a stop comamnd is manually issued. + gWorkDoneEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + if (!gWorkDoneEvent) + { + ReportSvcStatus(SERVICE_STOPPED, 1, 0); + StartTerminationThread(); + return; + } - // Initialization complete and we're about to start working on - // the actual command. Report the service state as running to the SCM. - ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + // Initialization complete and we're about to start working on + // the actual command. Report the service state as running to the SCM. + ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); - // The service command was executed, stop logging and set an event - // to indicate the work is done in case someone is waiting on a - // service stop operation. - ExecuteServiceCommand(argc, argv); - LogFinish(); + // The service command was executed, stop logging and set an event + // to indicate the work is done in case someone is waiting on a + // service stop operation. + ExecuteServiceCommand(argc, argv); + LogFinish(); - SetEvent(gWorkDoneEvent); + SetEvent(gWorkDoneEvent); - // If we aren't already in a stopping state then tell the SCM we're stopped - // now. If we are already in a stopping state then the SERVICE_STOPPED state - // will be set by the SvcCtrlHandler. - if (!gServiceControlStopping) { - ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); - StartTerminationThread(); - } + // If we aren't already in a stopping state then tell the SCM we're stopped + // now. If we are already in a stopping state then the SERVICE_STOPPED state + // will be set by the SvcCtrlHandler. + if (!gServiceControlStopping) + { + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + StartTerminationThread(); + } } /** @@ -312,29 +341,35 @@ ReportSvcStatus(DWORD currentState, DWORD exitCode, DWORD waitHint) { - static DWORD dwCheckPoint = 1; - - gSvcStatus.dwCurrentState = currentState; - gSvcStatus.dwWin32ExitCode = exitCode; - gSvcStatus.dwWaitHint = waitHint; - - if (SERVICE_START_PENDING == currentState || - SERVICE_STOP_PENDING == currentState) { - gSvcStatus.dwControlsAccepted = 0; - } else { - gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_SHUTDOWN; - } - - if ((SERVICE_RUNNING == currentState) || - (SERVICE_STOPPED == currentState)) { - gSvcStatus.dwCheckPoint = 0; - } else { - gSvcStatus.dwCheckPoint = dwCheckPoint++; - } - - // Report the status of the service to the SCM. - SetServiceStatus(gSvcStatusHandle, &gSvcStatus); + static DWORD dwCheckPoint = 1; + + gSvcStatus.dwCurrentState = currentState; + gSvcStatus.dwWin32ExitCode = exitCode; + gSvcStatus.dwWaitHint = waitHint; + + if (SERVICE_START_PENDING == currentState || + SERVICE_STOP_PENDING == currentState) + { + gSvcStatus.dwControlsAccepted = 0; + } + else + { + gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN; + } + + if ((SERVICE_RUNNING == currentState) || + (SERVICE_STOPPED == currentState)) + { + gSvcStatus.dwCheckPoint = 0; + } + else + { + gSvcStatus.dwCheckPoint = dwCheckPoint++; + } + + // Report the status of the service to the SCM. + SetServiceStatus(gSvcStatusHandle, &gSvcStatus); } /** @@ -344,14 +379,16 @@ ReportSvcStatus(DWORD currentState, DWORD WINAPI StopServiceAndWaitForCommandThread(LPVOID) { - do { - ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000); - } while(WaitForSingleObject(gWorkDoneEvent, 100) == WAIT_TIMEOUT); - CloseHandle(gWorkDoneEvent); - gWorkDoneEvent = nullptr; - ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); - StartTerminationThread(); - return 0; + do + { + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000); + } + while (WaitForSingleObject(gWorkDoneEvent, 100) == WAIT_TIMEOUT); + CloseHandle(gWorkDoneEvent); + gWorkDoneEvent = nullptr; + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + StartTerminationThread(); + return 0; } /** @@ -361,35 +398,41 @@ StopServiceAndWaitForCommandThread(LPVOID) void WINAPI SvcCtrlHandler(DWORD dwCtrl) { - // After a SERVICE_CONTROL_STOP there should be no more commands sent to - // the SvcCtrlHandler. - if (gServiceControlStopping) { - return; - } - - // Handle the requested control code. - switch(dwCtrl) { - case SERVICE_CONTROL_SHUTDOWN: - case SERVICE_CONTROL_STOP: { - gServiceControlStopping = true; - ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000); - - // The SvcCtrlHandler thread should not spend more than 30 seconds in - // shutdown so we spawn a new thread for stopping the service - HANDLE thread = CreateThread(nullptr, 0, - StopServiceAndWaitForCommandThread, - nullptr, 0, nullptr); - if (thread) { - CloseHandle(thread); - } else { - // Couldn't start the thread so just call the stop ourselves. - // If it happens to take longer than 30 seconds the caller will - // get an error. - StopServiceAndWaitForCommandThread(nullptr); - } + // After a SERVICE_CONTROL_STOP there should be no more commands sent to + // the SvcCtrlHandler. + if (gServiceControlStopping) + { + return; + } + + // Handle the requested control code. + switch (dwCtrl) + { + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + { + gServiceControlStopping = true; + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000); + + // The SvcCtrlHandler thread should not spend more than 30 seconds in + // shutdown so we spawn a new thread for stopping the service + HANDLE thread = CreateThread(nullptr, 0, + StopServiceAndWaitForCommandThread, + nullptr, 0, nullptr); + if (thread) + { + CloseHandle(thread); + } + else + { + // Couldn't start the thread so just call the stop ourselves. + // If it happens to take longer than 30 seconds the caller will + // get an error. + StopServiceAndWaitForCommandThread(nullptr); + } + } + break; + default: + break; } - break; - default: - break; - } } diff --git a/onlineupdate/source/service/maintenanceservice.hxx b/onlineupdate/source/service/maintenanceservice.hxx index 9e02914a0e7b..af5db9e29dae 100644 --- a/onlineupdate/source/service/maintenanceservice.hxx +++ b/onlineupdate/source/service/maintenanceservice.hxx @@ -5,6 +5,6 @@ void WINAPI SvcMain(DWORD dwArgc, LPWSTR *lpszArgv); void SvcInit(DWORD dwArgc, LPWSTR *lpszArgv); void WINAPI SvcCtrlHandler(DWORD dwCtrl); -void ReportSvcStatus(DWORD dwCurrentState, - DWORD dwWin32ExitCode, +void ReportSvcStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode, DWORD dwWaitHint); diff --git a/onlineupdate/source/service/registrycertificates.cxx b/onlineupdate/source/service/registrycertificates.cxx index f44fb3427d00..ea0905cea888 100644 --- a/onlineupdate/source/service/registrycertificates.cxx +++ b/onlineupdate/source/service/registrycertificates.cxx @@ -55,115 +55,127 @@ struct AutoRegKey BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath) { - WCHAR maintenanceServiceKey[MAX_PATH + 1]; - if (!CalculateRegistryPathFromFilePath(basePathForUpdate, - maintenanceServiceKey)) { - return FALSE; - } - - // We use KEY_WOW64_64KEY to always force 64-bit view. - // The user may have both x86 and x64 applications installed - // which each register information. We need a consistent place - // to put those certificate attributes in and hence why we always - // force the non redirected registry under Wow6432Node. - // This flag is ignored on 32bit systems. - HKEY baseKeyRaw; - LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, - maintenanceServiceKey, 0, - KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not open key. (%d)", retCode)); - // Our tests run with a different apply directory for each test. - // We use this registry key on our test slaves to store the - // allowed name/issuers. - retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, - TEST_ONLY_FALLBACK_KEY_PATH, 0, - KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not open fallback key. (%d)", retCode)); - return FALSE; - } - } - AutoRegKey baseKey(baseKeyRaw); - - // Get the number of subkeys. - DWORD subkeyCount = 0; - retCode = RegQueryInfoKeyW(baseKey.get(), nullptr, nullptr, nullptr, &subkeyCount, - nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not query info key. (%d)", retCode)); - return FALSE; - } - - // Enumerate the subkeys, each subkey represents an allowed certificate. - for (DWORD i = 0; i < subkeyCount; i++) { - WCHAR subkeyBuffer[MAX_KEY_LENGTH]; - DWORD subkeyBufferCount = MAX_KEY_LENGTH; - retCode = RegEnumKeyExW(baseKey.get(), i, subkeyBuffer, - &subkeyBufferCount, nullptr, - nullptr, nullptr, nullptr); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not enum certs. (%d)", retCode)); - return FALSE; + WCHAR maintenanceServiceKey[MAX_PATH + 1]; + if (!CalculateRegistryPathFromFilePath(basePathForUpdate, + maintenanceServiceKey)) + { + return FALSE; } - // Open the subkey for the current certificate - HKEY subKeyRaw; - retCode = RegOpenKeyExW(baseKey.get(), - subkeyBuffer, - 0, - KEY_READ | KEY_WOW64_64KEY, - &subKeyRaw); - AutoRegKey subKey(subKeyRaw); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not open subkey. (%d)", retCode)); - continue; // Try the next subkey + // We use KEY_WOW64_64KEY to always force 64-bit view. + // The user may have both x86 and x64 applications installed + // which each register information. We need a consistent place + // to put those certificate attributes in and hence why we always + // force the non redirected registry under Wow6432Node. + // This flag is ignored on 32bit systems. + HKEY baseKeyRaw; + LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + maintenanceServiceKey, 0, + KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not open key. (%d)", retCode)); + // Our tests run with a different apply directory for each test. + // We use this registry key on our test slaves to store the + // allowed name/issuers. + retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + TEST_ONLY_FALLBACK_KEY_PATH, 0, + KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not open fallback key. (%d)", retCode)); + return FALSE; + } } - - const int MAX_CHAR_COUNT = 256; - DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); - WCHAR name[MAX_CHAR_COUNT] = { L'\0' }; - WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' }; - - // Get the name from the registry - retCode = RegQueryValueExW(subKey.get(), L"name", 0, nullptr, - (LPBYTE)name, &valueBufSize); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not obtain name from registry. (%d)", retCode)); - continue; // Try the next subkey + AutoRegKey baseKey(baseKeyRaw); + + // Get the number of subkeys. + DWORD subkeyCount = 0; + retCode = RegQueryInfoKeyW(baseKey.get(), nullptr, nullptr, nullptr, &subkeyCount, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not query info key. (%d)", retCode)); + return FALSE; } - // Get the issuer from the registry - valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); - retCode = RegQueryValueExW(subKey.get(), L"issuer", 0, nullptr, - (LPBYTE)issuer, &valueBufSize); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Could not obtain issuer from registry. (%d)", retCode)); - continue; // Try the next subkey - } + // Enumerate the subkeys, each subkey represents an allowed certificate. + for (DWORD i = 0; i < subkeyCount; i++) + { + WCHAR subkeyBuffer[MAX_KEY_LENGTH]; + DWORD subkeyBufferCount = MAX_KEY_LENGTH; + retCode = RegEnumKeyExW(baseKey.get(), i, subkeyBuffer, + &subkeyBufferCount, nullptr, + nullptr, nullptr, nullptr); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not enum certs. (%d)", retCode)); + return FALSE; + } - CertificateCheckInfo allowedCertificate = { - name, - issuer, - }; + // Open the subkey for the current certificate + HKEY subKeyRaw; + retCode = RegOpenKeyExW(baseKey.get(), + subkeyBuffer, + 0, + KEY_READ | KEY_WOW64_64KEY, + &subKeyRaw); + AutoRegKey subKey(subKeyRaw); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not open subkey. (%d)", retCode)); + continue; // Try the next subkey + } - retCode = CheckCertificateForPEFile(filePath, allowedCertificate); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Error on certificate check. (%d)", retCode)); - continue; // Try the next subkey - } + const int MAX_CHAR_COUNT = 256; + DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); + WCHAR name[MAX_CHAR_COUNT] = { L'\0' }; + WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' }; - retCode = VerifyCertificateTrustForFile(filePath); - if (retCode != ERROR_SUCCESS) { - LOG_WARN(("Error on certificate trust check. (%d)", retCode)); - continue; // Try the next subkey - } + // Get the name from the registry + retCode = RegQueryValueExW(subKey.get(), L"name", 0, nullptr, + (LPBYTE)name, &valueBufSize); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not obtain name from registry. (%d)", retCode)); + continue; // Try the next subkey + } + + // Get the issuer from the registry + valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR); + retCode = RegQueryValueExW(subKey.get(), L"issuer", 0, nullptr, + (LPBYTE)issuer, &valueBufSize); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Could not obtain issuer from registry. (%d)", retCode)); + continue; // Try the next subkey + } + + CertificateCheckInfo allowedCertificate = + { + name, + issuer, + }; - // Raise the roof, we found a match! - return TRUE; - } + retCode = CheckCertificateForPEFile(filePath, allowedCertificate); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Error on certificate check. (%d)", retCode)); + continue; // Try the next subkey + } + + retCode = VerifyCertificateTrustForFile(filePath); + if (retCode != ERROR_SUCCESS) + { + LOG_WARN(("Error on certificate trust check. (%d)", retCode)); + continue; // Try the next subkey + } + + // Raise the roof, we found a match! + return TRUE; + } - // No certificates match, :'( - return FALSE; + // No certificates match, :'( + return FALSE; } diff --git a/onlineupdate/source/service/resource.hxx b/onlineupdate/source/service/resource.hxx index 45619457c9aa..f1a1c383665d 100644 --- a/onlineupdate/source/service/resource.hxx +++ b/onlineupdate/source/service/resource.hxx @@ -9,7 +9,7 @@ #define IDI_DIALOG 1003 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 diff --git a/onlineupdate/source/service/servicebase.cxx b/onlineupdate/source/service/servicebase.cxx index 1b4f406f431e..4fb632878cec 100644 --- a/onlineupdate/source/service/servicebase.cxx +++ b/onlineupdate/source/service/servicebase.cxx @@ -18,68 +18,80 @@ BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent) { - sameContent = FALSE; - AutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ, + sameContent = FALSE; + AutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)); - if (file1 == INVALID_HANDLE_VALUE) { - return FALSE; - } - AutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ, + if (file1 == INVALID_HANDLE_VALUE) + { + return FALSE; + } + AutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)); - if (file2 == INVALID_HANDLE_VALUE) { - return FALSE; - } - - DWORD fileSize1 = GetFileSize(file1.get(), nullptr); - DWORD fileSize2 = GetFileSize(file2.get(), nullptr); - if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2) { - return FALSE; - } - - if (fileSize1 != fileSize2) { - // sameContent is already set to FALSE - return TRUE; - } - - char buf1[COMPARE_BLOCKSIZE]; - char buf2[COMPARE_BLOCKSIZE]; - DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE; - DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE; - DWORD readAmount; - for (DWORD i = 0; i < numBlocks; i++) { - if (!ReadFile(file1.get(), buf1, COMPARE_BLOCKSIZE, &readAmount, nullptr) || - readAmount != COMPARE_BLOCKSIZE) { - return FALSE; + if (file2 == INVALID_HANDLE_VALUE) + { + return FALSE; } - if (!ReadFile(file2.get(), buf2, COMPARE_BLOCKSIZE, &readAmount, nullptr) || - readAmount != COMPARE_BLOCKSIZE) { - return FALSE; + DWORD fileSize1 = GetFileSize(file1.get(), nullptr); + DWORD fileSize2 = GetFileSize(file2.get(), nullptr); + if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2) + { + return FALSE; } - if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE)) { - // sameContent is already set to FALSE - return TRUE; + if (fileSize1 != fileSize2) + { + // sameContent is already set to FALSE + return TRUE; } - } - if (leftOver) { - if (!ReadFile(file1.get(), buf1, leftOver, &readAmount, nullptr) || - readAmount != leftOver) { - return FALSE; - } + char buf1[COMPARE_BLOCKSIZE]; + char buf2[COMPARE_BLOCKSIZE]; + DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE; + DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE; + DWORD readAmount; + for (DWORD i = 0; i < numBlocks; i++) + { + if (!ReadFile(file1.get(), buf1, COMPARE_BLOCKSIZE, &readAmount, nullptr) || + readAmount != COMPARE_BLOCKSIZE) + { + return FALSE; + } + + if (!ReadFile(file2.get(), buf2, COMPARE_BLOCKSIZE, &readAmount, nullptr) || + readAmount != COMPARE_BLOCKSIZE) + { + return FALSE; + } - if (!ReadFile(file2.get(), buf2, leftOver, &readAmount, nullptr) || - readAmount != leftOver) { - return FALSE; + if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE)) + { + // sameContent is already set to FALSE + return TRUE; + } } - if (memcmp(buf1, buf2, leftOver)) { - // sameContent is already set to FALSE - return TRUE; + if (leftOver) + { + if (!ReadFile(file1.get(), buf1, leftOver, &readAmount, nullptr) || + readAmount != leftOver) + { + return FALSE; + } + + if (!ReadFile(file2.get(), buf2, leftOver, &readAmount, nullptr) || + readAmount != leftOver) + { + return FALSE; + } + + if (memcmp(buf1, buf2, leftOver)) + { + // sameContent is already set to FALSE + return TRUE; + } } - } - sameContent = TRUE; - return TRUE; + sameContent = TRUE; + return TRUE; } diff --git a/onlineupdate/source/service/servicebase.hxx b/onlineupdate/source/service/servicebase.hxx index abf88a122a1d..c47f966ad551 100644 --- a/onlineupdate/source/service/servicebase.hxx +++ b/onlineupdate/source/service/servicebase.hxx @@ -14,7 +14,7 @@ BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent); #define COMPARE_BLOCKSIZE 32768 // The following string resource value is used to uniquely identify the signed -// Mozilla application as an updater. Before the maintenance service will +// Mozilla application as an updater. Before the maintenance service will // execute the updater it must have this updater identity string in its string // table. No other signed Mozilla product will have this string table value. #define UPDATER_IDENTITY_STRING \ diff --git a/onlineupdate/source/service/serviceinstall.cxx b/onlineupdate/source/service/serviceinstall.cxx index cf7fef354186..a225e9445544 100644 --- a/onlineupdate/source/service/serviceinstall.cxx +++ b/onlineupdate/source/service/serviceinstall.cxx @@ -73,19 +73,20 @@ static int ReadMaintenanceServiceStrings(LPCWSTR path, MaintenanceServiceStringTable *results) { - // Read in the maintenance service description string if specified. - const unsigned int kNumStrings = 1; - const char *kServiceKeys = "MozillaMaintenanceDescription\0"; - char serviceStrings[kNumStrings][MAX_TEXT_LEN]; - int result = ReadStrings(path, kServiceKeys, - kNumStrings, serviceStrings); - if (result != OK) { - serviceStrings[0][0] = '\0'; - } - strncpy(results->serviceDescription, - serviceStrings[0], MAX_TEXT_LEN - 1); - results->serviceDescription[MAX_TEXT_LEN - 1] = '\0'; - return result; + // Read in the maintenance service description string if specified. + const unsigned int kNumStrings = 1; + const char *kServiceKeys = "MozillaMaintenanceDescription\0"; + char serviceStrings[kNumStrings][MAX_TEXT_LEN]; + int result = ReadStrings(path, kServiceKeys, + kNumStrings, serviceStrings); + if (result != OK) + { + serviceStrings[0][0] = '\0'; + } + strncpy(results->serviceDescription, + serviceStrings[0], MAX_TEXT_LEN - 1); + results->serviceDescription[MAX_TEXT_LEN - 1] = '\0'; + return result; } /** @@ -103,30 +104,32 @@ static BOOL GetVersionNumberFromPath(LPWSTR path, DWORD &A, DWORD &B, DWORD &C, DWORD &D) { - DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0); - std::unique_ptr<char[]> fileVersionInfo(new char[fileVersionInfoSize]); - if (!GetFileVersionInfoW(path, 0, fileVersionInfoSize, - fileVersionInfo.get())) { - LOG_WARN(("Could not obtain file info of old service. (%d)", - GetLastError())); - return FALSE; - } - - VS_FIXEDFILEINFO *fixedFileInfo = - reinterpret_cast<VS_FIXEDFILEINFO *>(fileVersionInfo.get()); - UINT size; - if (!VerQueryValueW(fileVersionInfo.get(), L"\\", - reinterpret_cast<LPVOID*>(&fixedFileInfo), &size)) { - LOG_WARN(("Could not query file version info of old service. (%d)", - GetLastError())); - return FALSE; - } - - A = HIWORD(fixedFileInfo->dwFileVersionMS); - B = LOWORD(fixedFileInfo->dwFileVersionMS); - C = HIWORD(fixedFileInfo->dwFileVersionLS); - D = LOWORD(fixedFileInfo->dwFileVersionLS); - return TRUE; + DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0); + std::unique_ptr<char[]> fileVersionInfo(new char[fileVersionInfoSize]); + if (!GetFileVersionInfoW(path, 0, fileVersionInfoSize, + fileVersionInfo.get())) + { + LOG_WARN(("Could not obtain file info of old service. (%d)", + GetLastError())); + return FALSE; + } + + VS_FIXEDFILEINFO *fixedFileInfo = + reinterpret_cast<VS_FIXEDFILEINFO *>(fileVersionInfo.get()); + UINT size; + if (!VerQueryValueW(fileVersionInfo.get(), L"\\", + reinterpret_cast<LPVOID*>(&fixedFileInfo), &size)) + { + LOG_WARN(("Could not query file version info of old service. (%d)", + GetLastError())); + return FALSE; + } + + A = HIWORD(fixedFileInfo->dwFileVersionMS); + B = LOWORD(fixedFileInfo->dwFileVersionMS); + C = HIWORD(fixedFileInfo->dwFileVersionLS); + D = LOWORD(fixedFileInfo->dwFileVersionLS); + return TRUE; } /** @@ -140,63 +143,70 @@ GetVersionNumberFromPath(LPWSTR path, DWORD &A, DWORD &B, BOOL UpdateServiceDescription(SC_HANDLE serviceHandle) { - WCHAR updaterINIPath[MAX_PATH + 1]; - if (!GetModuleFileNameW(nullptr, updaterINIPath, - sizeof(updaterINIPath) / - sizeof(updaterINIPath[0]))) { - LOG_WARN(("Could not obtain module filename when attempting to " - "modify service description. (%d)", GetLastError())); - return FALSE; - } - - if (!PathRemoveFileSpecW(updaterINIPath)) { - LOG_WARN(("Could not remove file spec when attempting to " - "modify service description. (%d)", GetLastError())); - return FALSE; - } - - if (!PathAppendSafe(updaterINIPath, L"updater.ini")) { - LOG_WARN(("Could not append updater.ini filename when attempting to " - "modify service description. (%d)", GetLastError())); - return FALSE; - } - - if (GetFileAttributesW(updaterINIPath) == INVALID_FILE_ATTRIBUTES) { - LOG_WARN(("updater.ini file does not exist, will not modify " - "service description. (%d)", GetLastError())); - return FALSE; - } - - MaintenanceServiceStringTable serviceStrings; - int rv = ReadMaintenanceServiceStrings(updaterINIPath, &serviceStrings); - if (rv != OK || !strlen(serviceStrings.serviceDescription)) { - LOG_WARN(("updater.ini file does not contain a maintenance " - "service description.")); - return FALSE; - } - - WCHAR serviceDescription[MAX_TEXT_LEN]; - if (!MultiByteToWideChar(CP_UTF8, 0, - serviceStrings.serviceDescription, -1, - serviceDescription, - sizeof(serviceDescription) / - sizeof(serviceDescription[0]))) { - LOG_WARN(("Could not convert description to wide string format. (%d)", - GetLastError())); - return FALSE; - } - - SERVICE_DESCRIPTIONW descriptionConfig; - descriptionConfig.lpDescription = serviceDescription; - if (!ChangeServiceConfig2W(serviceHandle, - SERVICE_CONFIG_DESCRIPTION, - &descriptionConfig)) { - LOG_WARN(("Could not change service config. (%d)", GetLastError())); - return FALSE; - } - - LOG(("The service description was updated successfully.")); - return TRUE; + WCHAR updaterINIPath[MAX_PATH + 1]; + if (!GetModuleFileNameW(nullptr, updaterINIPath, + sizeof(updaterINIPath) / + sizeof(updaterINIPath[0]))) + { + LOG_WARN(("Could not obtain module filename when attempting to " + "modify service description. (%d)", GetLastError())); + return FALSE; + } + + if (!PathRemoveFileSpecW(updaterINIPath)) + { + LOG_WARN(("Could not remove file spec when attempting to " + "modify service description. (%d)", GetLastError())); + return FALSE; + } + + if (!PathAppendSafe(updaterINIPath, L"updater.ini")) + { + LOG_WARN(("Could not append updater.ini filename when attempting to " + "modify service description. (%d)", GetLastError())); + return FALSE; + } + + if (GetFileAttributesW(updaterINIPath) == INVALID_FILE_ATTRIBUTES) + { + LOG_WARN(("updater.ini file does not exist, will not modify " + "service description. (%d)", GetLastError())); + return FALSE; + } + + MaintenanceServiceStringTable serviceStrings; + int rv = ReadMaintenanceServiceStrings(updaterINIPath, &serviceStrings); + if (rv != OK || !strlen(serviceStrings.serviceDescription)) + { + LOG_WARN(("updater.ini file does not contain a maintenance " + "service description.")); + return FALSE; + } + + WCHAR serviceDescription[MAX_TEXT_LEN]; + if (!MultiByteToWideChar(CP_UTF8, 0, + serviceStrings.serviceDescription, -1, + serviceDescription, + sizeof(serviceDescription) / + sizeof(serviceDescription[0]))) + { + LOG_WARN(("Could not convert description to wide string format. (%d)", + GetLastError())); + return FALSE; + } + + SERVICE_DESCRIPTIONW descriptionConfig; + descriptionConfig.lpDescription = serviceDescription; + if (!ChangeServiceConfig2W(serviceHandle, + SERVICE_CONFIG_DESCRIPTION, + &descriptionConfig)) + { + LOG_WARN(("Could not change service config. (%d)", GetLastError())); + return FALSE; + } + + LOG(("The service description was updated successfully.")); + return TRUE; } /** @@ -213,54 +223,58 @@ FixServicePath(SC_HANDLE service, LPCWSTR currentServicePath, BOOL &servicePathWasWrong) { - // When we originally upgraded the MozillaMaintenance service we - // would uninstall the service on each upgrade. This had an - // intermittent error which could cause the service to use the file - // maintenanceservice_tmp.exe as the install path. Only a small number - // of Nightly users would be affected by this, but we check for this - // state here and fix the user if they are affected. - // - // We also fix the path in the case of the path not being quoted. - size_t currentServicePathLen = wcslen(currentServicePath); - bool doesServiceHaveCorrectPath = - currentServicePathLen > 2 && - !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe") && - currentServicePath[0] == L'\"' && - currentServicePath[currentServicePathLen - 1] == L'\"'; - - if (doesServiceHaveCorrectPath) { - LOG(("The MozillaMaintenance service path is correct.")); - servicePathWasWrong = FALSE; + // When we originally upgraded the MozillaMaintenance service we + // would uninstall the service on each upgrade. This had an + // intermittent error which could cause the service to use the file + // maintenanceservice_tmp.exe as the install path. Only a small number + // of Nightly users would be affected by this, but we check for this + // state here and fix the user if they are affected. + // + // We also fix the path in the case of the path not being quoted. + size_t currentServicePathLen = wcslen(currentServicePath); + bool doesServiceHaveCorrectPath = + currentServicePathLen > 2 && + !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe") && + currentServicePath[0] == L'\"' && + currentServicePath[currentServicePathLen - 1] == L'\"'; + + if (doesServiceHaveCorrectPath) + { + LOG(("The MozillaMaintenance service path is correct.")); + servicePathWasWrong = FALSE; + return TRUE; + } + // This is a recoverable situation so not logging as a warning + LOG(("The MozillaMaintenance path is NOT correct. It was: %ls", + currentServicePath)); + + servicePathWasWrong = TRUE; + WCHAR fixedPath[MAX_PATH + 1] = { L'\0' }; + wcsncpy(fixedPath, currentServicePath, MAX_PATH); + PathUnquoteSpacesW(fixedPath); + if (!PathRemoveFileSpecW(fixedPath)) + { + LOG_WARN(("Couldn't remove file spec. (%d)", GetLastError())); + return FALSE; + } + if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) + { + LOG_WARN(("Couldn't append file spec. (%d)", GetLastError())); + return FALSE; + } + PathQuoteSpacesW(fixedPath); + + + if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, fixedPath, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr)) + { + LOG_WARN(("Could not fix service path. (%d)", GetLastError())); + return FALSE; + } + + LOG(("Fixed service path to: %ls.", fixedPath)); return TRUE; - } - // This is a recoverable situation so not logging as a warning - LOG(("The MozillaMaintenance path is NOT correct. It was: %ls", - currentServicePath)); - - servicePathWasWrong = TRUE; - WCHAR fixedPath[MAX_PATH + 1] = { L'\0' }; - wcsncpy(fixedPath, currentServicePath, MAX_PATH); - PathUnquoteSpacesW(fixedPath); - if (!PathRemoveFileSpecW(fixedPath)) { - LOG_WARN(("Couldn't remove file spec. (%d)", GetLastError())); - return FALSE; - } - if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) { - LOG_WARN(("Couldn't append file spec. (%d)", GetLastError())); - return FALSE; - } - PathQuoteSpacesW(fixedPath); - - - if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, - SERVICE_NO_CHANGE, fixedPath, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr)) { - LOG_WARN(("Could not fix service path. (%d)", GetLastError())); - return FALSE; - } - - LOG(("Fixed service path to: %ls.", fixedPath)); - return TRUE; } /** @@ -274,263 +288,299 @@ FixServicePath(SC_HANDLE service, BOOL SvcInstall(SvcInstallAction action) { - // Get a handle to the local computer SCM database with full access rights. - AutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, - SC_MANAGER_ALL_ACCESS)); - if (!schSCManager) { - LOG_WARN(("Could not open service manager. (%d)", GetLastError())); - return FALSE; - } - - WCHAR newServiceBinaryPath[MAX_PATH + 1]; - if (!GetModuleFileNameW(nullptr, newServiceBinaryPath, - sizeof(newServiceBinaryPath) / - sizeof(newServiceBinaryPath[0]))) { - LOG_WARN(("Could not obtain module filename when attempting to " - "install service. (%d)", - GetLastError())); - return FALSE; - } - - // Check if we already have the service installed. - AutoServiceHandle schService(OpenServiceW(schSCManager.get(), - SVC_NAME, - SERVICE_ALL_ACCESS)); - DWORD lastError = GetLastError(); - if (!schService && ERROR_SERVICE_DOES_NOT_EXIST != lastError) { - // The service exists but we couldn't open it - LOG_WARN(("Could not open service. (%d)", GetLastError())); - return FALSE; - } - - if (schService) { - // The service exists but it may not have the correct permissions. - // This could happen if the permissions were not set correctly originally - // or have been changed after the installation. This will reset the - // permissions back to allow limited user accounts. - if (!SetUserAccessServiceDACL(schService.get())) { - LOG_WARN(("Could not reset security ACE on service handle. It might not be " - "possible to start the service. This error should never " - "happen. (%d)", GetLastError())); - } - - // The service exists and we opened it - DWORD bytesNeeded; - if (!QueryServiceConfigW(schService.get(), nullptr, 0, &bytesNeeded) && - GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - LOG_WARN(("Could not determine buffer size for query service config. (%d)", - GetLastError())); - return FALSE; - } - - // Get the service config information, in particular we want the binary - // path of the service. - std::unique_ptr<char[]> serviceConfigBuffer(new char[bytesNeeded]); - if (!QueryServiceConfigW(schService.get(), - reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), - bytesNeeded, &bytesNeeded)) { - LOG_WARN(("Could open service but could not query service config. (%d)", - GetLastError())); - return FALSE; - } - QUERY_SERVICE_CONFIGW &serviceConfig = - *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()); - - // Check if we need to fix the service path - BOOL servicePathWasWrong; - static BOOL alreadyCheckedFixServicePath = FALSE; - if (!alreadyCheckedFixServicePath) { - if (!FixServicePath(schService.get(), serviceConfig.lpBinaryPathName, - servicePathWasWrong)) { - LOG_WARN(("Could not fix service path. This should never happen. (%d)", + // Get a handle to the local computer SCM database with full access rights. + AutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, + SC_MANAGER_ALL_ACCESS)); + if (!schSCManager) + { + LOG_WARN(("Could not open service manager. (%d)", GetLastError())); + return FALSE; + } + + WCHAR newServiceBinaryPath[MAX_PATH + 1]; + if (!GetModuleFileNameW(nullptr, newServiceBinaryPath, + sizeof(newServiceBinaryPath) / + sizeof(newServiceBinaryPath[0]))) + { + LOG_WARN(("Could not obtain module filename when attempting to " + "install service. (%d)", GetLastError())); - // True is returned because the service is pointing to - // maintenanceservice_tmp.exe so it actually was upgraded to the - // newest installed service. - return TRUE; - } else if (servicePathWasWrong) { - // Now that the path is fixed we should re-attempt the install. - // This current process' image path is maintenanceservice_tmp.exe. - // The service used to point to maintenanceservice_tmp.exe. - // The service was just fixed to point to maintenanceservice.exe. - // Re-attempting an install from scratch will work as normal. - alreadyCheckedFixServicePath = TRUE; - LOG(("Restarting install action: %d", action)); - return SvcInstall(action); - } - } - - // Ensure the service path is not quoted. We own this memory and know it to - // be large enough for the quoted path, so it is large enough for the - // unquoted path. This function cannot fail. - PathUnquoteSpacesW(serviceConfig.lpBinaryPathName); - - // Obtain the existing maintenanceservice file's version number and - // the new file's version number. Versions are in the format of - // A.B.C.D. - DWORD existingA, existingB, existingC, existingD; - DWORD newA, newB, newC, newD; - BOOL obtainedExistingVersionInfo = - GetVersionNumberFromPath(serviceConfig.lpBinaryPathName, - existingA, existingB, - existingC, existingD); - if (!GetVersionNumberFromPath(newServiceBinaryPath, newA, - newB, newC, newD)) { - LOG_WARN(("Could not obtain version number from new path")); - return FALSE; - } - - // Check if we need to replace the old binary with the new one - // If we couldn't get the old version info then we assume we should - // replace it. - if (ForceInstallSvc == action || - !obtainedExistingVersionInfo || - (existingA < newA) || - (existingA == newA && existingB < newB) || - (existingA == newA && existingB == newB && - existingC < newC) || - (existingA == newA && existingB == newB && - existingC == newC && existingD < newD)) { - - // We have a newer updater, so update the description from the INI file. - UpdateServiceDescription(schService.get()); - - schService.reset(); - if (!StopService()) { return FALSE; - } + } - if (!wcscmp(newServiceBinaryPath, serviceConfig.lpBinaryPathName)) { - LOG(("File is already in the correct location, no action needed for " - "upgrade. The path is: \"%ls\"", newServiceBinaryPath)); - return TRUE; - } - - BOOL result = TRUE; - - // Attempt to copy the new binary over top the existing binary. - // If there is an error we try to move it out of the way and then - // copy it in. First try the safest / easiest way to overwrite the file. - if (!CopyFileW(newServiceBinaryPath, - serviceConfig.lpBinaryPathName, FALSE)) { - LOG_WARN(("Could not overwrite old service binary file. " - "This should never happen, but if it does the next " - "upgrade will fix it, the service is not a critical " - "component that needs to be installed for upgrades " - "to work. (%d)", GetLastError())); - - // We rename the last 3 filename chars in an unsafe way. Manually - // verify there are more than 3 chars for safe failure in MoveFileExW. - const size_t len = wcslen(serviceConfig.lpBinaryPathName); - if (len > 3) { - // Calculate the temp file path that we're moving the file to. This - // is the same as the proper service path but with a .old extension. - LPWSTR oldServiceBinaryTempPath = - new WCHAR[len + 1]; - memset(oldServiceBinaryTempPath, 0, (len + 1) * sizeof (WCHAR)); - wcsncpy(oldServiceBinaryTempPath, serviceConfig.lpBinaryPathName, len); - // Rename the last 3 chars to 'old' - wcsncpy(oldServiceBinaryTempPath + len - 3, L"old", 3); - - // Move the current (old) service file to the temp path. - if (MoveFileExW(serviceConfig.lpBinaryPathName, - oldServiceBinaryTempPath, - MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { - // The old binary is moved out of the way, copy in the new one. + // Check if we already have the service installed. + AutoServiceHandle schService(OpenServiceW(schSCManager.get(), + SVC_NAME, + SERVICE_ALL_ACCESS)); + DWORD lastError = GetLastError(); + if (!schService && ERROR_SERVICE_DOES_NOT_EXIST != lastError) + { + // The service exists but we couldn't open it + LOG_WARN(("Could not open service. (%d)", GetLastError())); + return FALSE; + } + + if (schService) + { + // The service exists but it may not have the correct permissions. + // This could happen if the permissions were not set correctly originally + // or have been changed after the installation. This will reset the + // permissions back to allow limited user accounts. + if (!SetUserAccessServiceDACL(schService.get())) + { + LOG_WARN(("Could not reset security ACE on service handle. It might not be " + "possible to start the service. This error should never " + "happen. (%d)", GetLastError())); + } + + // The service exists and we opened it + DWORD bytesNeeded; + if (!QueryServiceConfigW(schService.get(), nullptr, 0, &bytesNeeded) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + LOG_WARN(("Could not determine buffer size for query service config. (%d)", + GetLastError())); + return FALSE; + } + + // Get the service config information, in particular we want the binary + // path of the service. + std::unique_ptr<char[]> serviceConfigBuffer(new char[bytesNeeded]); + if (!QueryServiceConfigW(schService.get(), + reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), + bytesNeeded, &bytesNeeded)) + { + LOG_WARN(("Could open service but could not query service config. (%d)", + GetLastError())); + return FALSE; + } + QUERY_SERVICE_CONFIGW &serviceConfig = + *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()); + + // Check if we need to fix the service path + BOOL servicePathWasWrong; + static BOOL alreadyCheckedFixServicePath = FALSE; + if (!alreadyCheckedFixServicePath) + { + if (!FixServicePath(schService.get(), serviceConfig.lpBinaryPathName, + servicePathWasWrong)) + { + LOG_WARN(("Could not fix service path. This should never happen. (%d)", + GetLastError())); + // True is returned because the service is pointing to + // maintenanceservice_tmp.exe so it actually was upgraded to the + // newest installed service. + return TRUE; + } + else if (servicePathWasWrong) + { + // Now that the path is fixed we should re-attempt the install. + // This current process' image path is maintenanceservice_tmp.exe. + // The service used to point to maintenanceservice_tmp.exe. + // The service was just fixed to point to maintenanceservice.exe. + // Re-attempting an install from scratch will work as normal. + alreadyCheckedFixServicePath = TRUE; + LOG(("Restarting install action: %d", action)); + return SvcInstall(action); + } + } + + // Ensure the service path is not quoted. We own this memory and know it to + // be large enough for the quoted path, so it is large enough for the + // unquoted path. This function cannot fail. + PathUnquoteSpacesW(serviceConfig.lpBinaryPathName); + + // Obtain the existing maintenanceservice file's version number and + // the new file's version number. Versions are in the format of + // A.B.C.D. + DWORD existingA, existingB, existingC, existingD; + DWORD newA, newB, newC, newD; + BOOL obtainedExistingVersionInfo = + GetVersionNumberFromPath(serviceConfig.lpBinaryPathName, + existingA, existingB, + existingC, existingD); + if (!GetVersionNumberFromPath(newServiceBinaryPath, newA, + newB, newC, newD)) + { + LOG_WARN(("Could not obtain version number from new path")); + return FALSE; + } + + // Check if we need to replace the old binary with the new one + // If we couldn't get the old version info then we assume we should + // replace it. + if (ForceInstallSvc == action || + !obtainedExistingVersionInfo || + (existingA < newA) || + (existingA == newA && existingB < newB) || + (existingA == newA && existingB == newB && + existingC < newC) || + (existingA == newA && existingB == newB && + existingC == newC && existingD < newD)) + { + + // We have a newer updater, so update the description from the INI file. + UpdateServiceDescription(schService.get()); + + schService.reset(); + if (!StopService()) + { + return FALSE; + } + + if (!wcscmp(newServiceBinaryPath, serviceConfig.lpBinaryPathName)) + { + LOG(("File is already in the correct location, no action needed for " + "upgrade. The path is: \"%ls\"", newServiceBinaryPath)); + return TRUE; + } + + BOOL result = TRUE; + + // Attempt to copy the new binary over top the existing binary. + // If there is an error we try to move it out of the way and then + // copy it in. First try the safest / easiest way to overwrite the file. if (!CopyFileW(newServiceBinaryPath, - serviceConfig.lpBinaryPathName, FALSE)) { - // It is best to leave the old service binary in this condition. - LOG_WARN(("The new service binary could not be copied in." - " The service will not be upgraded.")); - result = FALSE; - } else { - LOG(("The new service binary was copied in by first moving the" - " old one out of the way.")); + serviceConfig.lpBinaryPathName, FALSE)) + { + LOG_WARN(("Could not overwrite old service binary file. " + "This should never happen, but if it does the next " + "upgrade will fix it, the service is not a critical " + "component that needs to be installed for upgrades " + "to work. (%d)", GetLastError())); + + // We rename the last 3 filename chars in an unsafe way. Manually + // verify there are more than 3 chars for safe failure in MoveFileExW. + const size_t len = wcslen(serviceConfig.lpBinaryPathName); + if (len > 3) + { + // Calculate the temp file path that we're moving the file to. This + // is the same as the proper service path but with a .old extension. + LPWSTR oldServiceBinaryTempPath = + new WCHAR[len + 1]; + memset(oldServiceBinaryTempPath, 0, (len + 1) * sizeof (WCHAR)); + wcsncpy(oldServiceBinaryTempPath, serviceConfig.lpBinaryPathName, len); + // Rename the last 3 chars to 'old' + wcsncpy(oldServiceBinaryTempPath + len - 3, L"old", 3); + + // Move the current (old) service file to the temp path. + if (MoveFileExW(serviceConfig.lpBinaryPathName, + oldServiceBinaryTempPath, + MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) + { + // The old binary is moved out of the way, copy in the new one. + if (!CopyFileW(newServiceBinaryPath, + serviceConfig.lpBinaryPathName, FALSE)) + { + // It is best to leave the old service binary in this condition. + LOG_WARN(("The new service binary could not be copied in." + " The service will not be upgraded.")); + result = FALSE; + } + else + { + LOG(("The new service binary was copied in by first moving the" + " old one out of the way.")); + } + + // Attempt to get rid of the old service temp path. + if (DeleteFileW(oldServiceBinaryTempPath)) + { + LOG(("The old temp service path was deleted: %ls.", + oldServiceBinaryTempPath)); + } + else + { + // The old temp path could not be removed. It will be removed + // the next time the user can't copy the binary in or on uninstall. + LOG_WARN(("The old temp service path was not deleted.")); + } + } + else + { + // It is best to leave the old service binary in this condition. + LOG_WARN(("Could not move old service file out of the way from:" + " \"%ls\" to \"%ls\". Service will not be upgraded. (%d)", + serviceConfig.lpBinaryPathName, + oldServiceBinaryTempPath, GetLastError())); + result = FALSE; + } + delete[] oldServiceBinaryTempPath; + } + else + { + // It is best to leave the old service binary in this condition. + LOG_WARN(("Service binary path was less than 3, service will" + " not be updated. This should never happen.")); + result = FALSE; + } + } + else + { + LOG(("The new service binary was copied in.")); } - // Attempt to get rid of the old service temp path. - if (DeleteFileW(oldServiceBinaryTempPath)) { - LOG(("The old temp service path was deleted: %ls.", - oldServiceBinaryTempPath)); - } else { - // The old temp path could not be removed. It will be removed - // the next time the user can't copy the binary in or on uninstall. - LOG_WARN(("The old temp service path was not deleted.")); + // We made a copy of ourselves to the existing location. + // The tmp file (the process of which we are executing right now) will be + // left over. Attempt to delete the file on the next reboot. + if (MoveFileExW(newServiceBinaryPath, nullptr, + MOVEFILE_DELAY_UNTIL_REBOOT)) + { + LOG(("Deleting the old file path on the next reboot: %ls.", + newServiceBinaryPath)); + } + else + { + LOG_WARN(("Call to delete the old file path failed: %ls.", + newServiceBinaryPath)); } - } else { - // It is best to leave the old service binary in this condition. - LOG_WARN(("Could not move old service file out of the way from:" - " \"%ls\" to \"%ls\". Service will not be upgraded. (%d)", - serviceConfig.lpBinaryPathName, - oldServiceBinaryTempPath, GetLastError())); - result = FALSE; - } - delete[] oldServiceBinaryTempPath; - } else { - // It is best to leave the old service binary in this condition. - LOG_WARN(("Service binary path was less than 3, service will" - " not be updated. This should never happen.")); - result = FALSE; + + return result; } - } else { - LOG(("The new service binary was copied in.")); - } - - // We made a copy of ourselves to the existing location. - // The tmp file (the process of which we are executing right now) will be - // left over. Attempt to delete the file on the next reboot. - if (MoveFileExW(newServiceBinaryPath, nullptr, - MOVEFILE_DELAY_UNTIL_REBOOT)) { - LOG(("Deleting the old file path on the next reboot: %ls.", - newServiceBinaryPath)); - } else { - LOG_WARN(("Call to delete the old file path failed: %ls.", - newServiceBinaryPath)); - } - - return result; - } - - // We don't need to copy ourselves to the existing location. - // The tmp file (the process of which we are executing right now) will be - // left over. Attempt to delete the file on the next reboot. - MoveFileExW(newServiceBinaryPath, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT); - - // nothing to do, we already have a newer service installed - return TRUE; - } - // If the service does not exist and we are upgrading, don't install it. - if (UpgradeSvc == action) { - // The service does not exist and we are upgrading, so don't install it + // We don't need to copy ourselves to the existing location. + // The tmp file (the process of which we are executing right now) will be + // left over. Attempt to delete the file on the next reboot. + MoveFileExW(newServiceBinaryPath, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT); + + // nothing to do, we already have a newer service installed + return TRUE; + } + + // If the service does not exist and we are upgrading, don't install it. + if (UpgradeSvc == action) + { + // The service does not exist and we are upgrading, so don't install it + return TRUE; + } + + // Quote the path only if it contains spaces. + PathQuoteSpacesW(newServiceBinaryPath); + // The service does not already exist so create the service as on demand + schService.reset(CreateServiceW(schSCManager.get(), SVC_NAME, SVC_DISPLAY_NAME, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + newServiceBinaryPath, nullptr, nullptr, + nullptr, nullptr, nullptr)); + if (!schService) + { + LOG_WARN(("Could not create Windows service. " + "This error should never happen since a service install " + "should only be called when elevated. (%d)", GetLastError())); + return FALSE; + } + + if (!SetUserAccessServiceDACL(schService.get())) + { + LOG_WARN(("Could not set security ACE on service handle, the service will not " + "be able to be started from unelevated processes. " + "This error should never happen. (%d)", + GetLastError())); + } + + UpdateServiceDescription(schService.get()); + return TRUE; - } - - // Quote the path only if it contains spaces. - PathQuoteSpacesW(newServiceBinaryPath); - // The service does not already exist so create the service as on demand - schService.reset(CreateServiceW(schSCManager.get(), SVC_NAME, SVC_DISPLAY_NAME, - SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, - SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, - newServiceBinaryPath, nullptr, nullptr, - nullptr, nullptr, nullptr)); - if (!schService) { - LOG_WARN(("Could not create Windows service. " - "This error should never happen since a service install " - "should only be called when elevated. (%d)", GetLastError())); - return FALSE; - } - - if (!SetUserAccessServiceDACL(schService.get())) { - LOG_WARN(("Could not set security ACE on service handle, the service will not " - "be able to be started from unelevated processes. " - "This error should never happen. (%d)", - GetLastError())); - } - - UpdateServiceDescription(schService.get()); - - return TRUE; } /** @@ -541,42 +591,45 @@ SvcInstall(SvcInstallAction action) BOOL StopService() { - // Get a handle to the local computer SCM database with full access rights. - AutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, - SC_MANAGER_ALL_ACCESS)); - if (!schSCManager) { - LOG_WARN(("Could not open service manager. (%d)", GetLastError())); - return FALSE; - } - - // Open the service - AutoServiceHandle schService(OpenServiceW(schSCManager.get(), SVC_NAME, - SERVICE_ALL_ACCESS)); - if (!schService) { - LOG_WARN(("Could not open service. (%d)", GetLastError())); - return FALSE; - } - - LOG(("Sending stop request...")); - SERVICE_STATUS status; - SetLastError(ERROR_SUCCESS); - if (!ControlService(schService.get(), SERVICE_CONTROL_STOP, &status) && - GetLastError() != ERROR_SERVICE_NOT_ACTIVE) { - LOG_WARN(("Error sending stop request. (%d)", GetLastError())); - } - - schSCManager.reset(); - schService.reset(); - - LOG(("Waiting for service stop...")); - DWORD lastState = WaitForServiceStop(SVC_NAME, 30); - - // The service can be in a stopped state but the exe still in use - // so make sure the process is really gone before proceeding - WaitForProcessExit(L"maintenanceservice.exe", 30); - LOG(("Done waiting for service stop, last service state: %d", lastState)); - - return lastState == SERVICE_STOPPED; + // Get a handle to the local computer SCM database with full access rights. + AutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, + SC_MANAGER_ALL_ACCESS)); + if (!schSCManager) + { + LOG_WARN(("Could not open service manager. (%d)", GetLastError())); + return FALSE; + } + + // Open the service + AutoServiceHandle schService(OpenServiceW(schSCManager.get(), SVC_NAME, + SERVICE_ALL_ACCESS)); + if (!schService) + { + LOG_WARN(("Could not open service. (%d)", GetLastError())); + return FALSE; + } + + LOG(("Sending stop request...")); + SERVICE_STATUS status; + SetLastError(ERROR_SUCCESS); + if (!ControlService(schService.get(), SERVICE_CONTROL_STOP, &status) && + GetLastError() != ERROR_SERVICE_NOT_ACTIVE) + { + LOG_WARN(("Error sending stop request. (%d)", GetLastError())); + } + + schSCManager.reset(); + schService.reset(); + + LOG(("Waiting for service stop...")); + DWORD lastState = WaitForServiceStop(SVC_NAME, 30); + + // The service can be in a stopped state but the exe still in use + // so make sure the process is really gone before proceeding + WaitForProcessExit(L"maintenanceservice.exe", 30); + LOG(("Done waiting for service stop, last service state: %d", lastState)); + + return lastState == SERVICE_STOPPED; } /** @@ -587,46 +640,55 @@ StopService() BOOL SvcUninstall() { - // Get a handle to the local computer SCM database with full access rights. - AutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, - SC_MANAGER_ALL_ACCESS)); - if (!schSCManager) { - LOG_WARN(("Could not open service manager. (%d)", GetLastError())); - return FALSE; - } - - // Open the service - AutoServiceHandle schService(OpenServiceW(schSCManager.get(), SVC_NAME, - SERVICE_ALL_ACCESS)); - if (!schService) { - LOG_WARN(("Could not open service. (%d)", GetLastError())); - return FALSE; - } - - //Stop the service so it deletes faster and so the uninstaller - // can actually delete its EXE. - DWORD totalWaitTime = 0; - SERVICE_STATUS status; - static const int maxWaitTime = 1000 * 60; // Never wait more than a minute - if (ControlService(schService.get(), SERVICE_CONTROL_STOP, &status)) { - do { - Sleep(status.dwWaitHint); - totalWaitTime += (status.dwWaitHint + 10); - if (status.dwCurrentState == SERVICE_STOPPED) { - break; - } else if (totalWaitTime > maxWaitTime) { - break; - } - } while (QueryServiceStatus(schService.get(), &status)); - } - - // Delete the service or mark it for deletion - BOOL deleted = DeleteService(schService.get()); - if (!deleted) { - deleted = (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE); - } - - return deleted; + // Get a handle to the local computer SCM database with full access rights. + AutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, + SC_MANAGER_ALL_ACCESS)); + if (!schSCManager) + { + LOG_WARN(("Could not open service manager. (%d)", GetLastError())); + return FALSE; + } + + // Open the service + AutoServiceHandle schService(OpenServiceW(schSCManager.get(), SVC_NAME, + SERVICE_ALL_ACCESS)); + if (!schService) + { + LOG_WARN(("Could not open service. (%d)", GetLastError())); + return FALSE; + } + + //Stop the service so it deletes faster and so the uninstaller + // can actually delete its EXE. + DWORD totalWaitTime = 0; + SERVICE_STATUS status; + static const int maxWaitTime = 1000 * 60; // Never wait more than a minute + if (ControlService(schService.get(), SERVICE_CONTROL_STOP, &status)) + { + do + { + Sleep(status.dwWaitHint); + totalWaitTime += (status.dwWaitHint + 10); + if (status.dwCurrentState == SERVICE_STOPPED) + { + break; + } + else if (totalWaitTime > maxWaitTime) + { + break; + } + } + while (QueryServiceStatus(schService.get(), &status)); + } + + // Delete the service or mark it for deletion + BOOL deleted = DeleteService(schService.get()); + if (!deleted) + { + deleted = (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE); + } + + return deleted; } /** @@ -638,16 +700,18 @@ SvcUninstall() BOOL SetUserAccessServiceDACL(SC_HANDLE hService) { - PACL pNewAcl = nullptr; - PSECURITY_DESCRIPTOR psd = nullptr; - DWORD lastError = SetUserAccessServiceDACL(hService, pNewAcl, psd); - if (pNewAcl) { - LocalFree((HLOCAL)pNewAcl); - } - if (psd) { - LocalFree((LPVOID)psd); - } - return ERROR_SUCCESS == lastError; + PACL pNewAcl = nullptr; + PSECURITY_DESCRIPTOR psd = nullptr; + DWORD lastError = SetUserAccessServiceDACL(hService, pNewAcl, psd); + if (pNewAcl) + { + LocalFree((HLOCAL)pNewAcl); + } + if (psd) + { + LocalFree((LPVOID)psd); + } + return ERROR_SUCCESS == lastError; } /** @@ -662,112 +726,124 @@ DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, PSECURITY_DESCRIPTOR psd) { - // Get the current security descriptor needed size - DWORD needed = 0; - if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, - &psd, 0, &needed)) { - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - LOG_WARN(("Could not query service object security size. (%d)", - GetLastError())); - return GetLastError(); + // Get the current security descriptor needed size + DWORD needed = 0; + if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, + &psd, 0, &needed)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + LOG_WARN(("Could not query service object security size. (%d)", + GetLastError())); + return GetLastError(); + } + + DWORD size = needed; + psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, size); + if (!psd) + { + LOG_WARN(("Could not allocate security descriptor. (%d)", + GetLastError())); + return ERROR_INSUFFICIENT_BUFFER; + } + + // Get the actual security descriptor now + if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, + psd, size, &needed)) + { + LOG_WARN(("Could not allocate security descriptor. (%d)", + GetLastError())); + return GetLastError(); + } } - DWORD size = needed; - psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, size); - if (!psd) { - LOG_WARN(("Could not allocate security descriptor. (%d)", - GetLastError())); - return ERROR_INSUFFICIENT_BUFFER; + // Get the current DACL from the security descriptor. + PACL pacl = nullptr; + BOOL bDaclPresent = FALSE; + BOOL bDaclDefaulted = FALSE; + if ( !GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, + &bDaclDefaulted)) + { + LOG_WARN(("Could not obtain DACL. (%d)", GetLastError())); + return GetLastError(); } - // Get the actual security descriptor now - if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, - psd, size, &needed)) { - LOG_WARN(("Could not allocate security descriptor. (%d)", - GetLastError())); - return GetLastError(); - } - } - - // Get the current DACL from the security descriptor. - PACL pacl = nullptr; - BOOL bDaclPresent = FALSE; - BOOL bDaclDefaulted = FALSE; - if ( !GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, - &bDaclDefaulted)) { - LOG_WARN(("Could not obtain DACL. (%d)", GetLastError())); - return GetLastError(); - } - - PSID sid; - DWORD SIDSize = SECURITY_MAX_SID_SIZE; - sid = LocalAlloc(LMEM_FIXED, SIDSize); - if (!sid) { - LOG_WARN(("Could not allocate SID memory. (%d)", GetLastError())); - return GetLastError(); - } - - if (!CreateWellKnownSid(WinBuiltinUsersSid, nullptr, sid, &SIDSize)) { - DWORD lastError = GetLastError(); - LOG_WARN(("Could not create well known SID. (%d)", lastError)); - LocalFree(sid); - return lastError; - } - - // Lookup the account name, the function fails if you don't pass in - // a buffer for the domain name but it's not used since we're using - // the built in account Sid. - SID_NAME_USE accountType; - WCHAR accountName[UNLEN + 1] = { L'\0' }; - WCHAR domainName[DNLEN + 1] = { L'\0' }; - DWORD accountNameSize = UNLEN + 1; - DWORD domainNameSize = DNLEN + 1; - if (!LookupAccountSidW(nullptr, sid, accountName, - &accountNameSize, - domainName, &domainNameSize, &accountType)) { - LOG_WARN(("Could not lookup account Sid, will try Users. (%d)", - GetLastError())); - wcsncpy(accountName, L"Users", UNLEN); - } - - // We already have the group name so we can get rid of the SID - FreeSid(sid); - sid = nullptr; - - // Build the ACE, BuildExplicitAccessWithName cannot fail so it is not logged. - EXPLICIT_ACCESS ea; - BuildExplicitAccessWithNameW(&ea, accountName, - SERVICE_START | SERVICE_STOP | GENERIC_READ, - SET_ACCESS, NO_INHERITANCE); - DWORD lastError = SetEntriesInAclW(1, (PEXPLICIT_ACCESS)&ea, pacl, &pNewAcl); - if (ERROR_SUCCESS != lastError) { - LOG_WARN(("Could not set entries in ACL. (%d)", lastError)); - return lastError; - } - - // Initialize a new security descriptor. - SECURITY_DESCRIPTOR sd; - if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { - LOG_WARN(("Could not initialize security descriptor. (%d)", - GetLastError())); - return GetLastError(); - } - - // Set the new DACL in the security descriptor. - if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE)) { - LOG_WARN(("Could not set security descriptor DACL. (%d)", - GetLastError())); - return GetLastError(); - } - - // Set the new security descriptor for the service object. - if (!SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, &sd)) { - LOG_WARN(("Could not set object security. (%d)", - GetLastError())); - return GetLastError(); - } - - // Woohoo, raise the roof - LOG(("User access was set successfully on the service.")); - return ERROR_SUCCESS; + PSID sid; + DWORD SIDSize = SECURITY_MAX_SID_SIZE; + sid = LocalAlloc(LMEM_FIXED, SIDSize); + if (!sid) + { + LOG_WARN(("Could not allocate SID memory. (%d)", GetLastError())); + return GetLastError(); + } + + if (!CreateWellKnownSid(WinBuiltinUsersSid, nullptr, sid, &SIDSize)) + { + DWORD lastError = GetLastError(); + LOG_WARN(("Could not create well known SID. (%d)", lastError)); + LocalFree(sid); + return lastError; + } + + // Lookup the account name, the function fails if you don't pass in + // a buffer for the domain name but it's not used since we're using + // the built in account Sid. + SID_NAME_USE accountType; + WCHAR accountName[UNLEN + 1] = { L'\0' }; + WCHAR domainName[DNLEN + 1] = { L'\0' }; + DWORD accountNameSize = UNLEN + 1; + DWORD domainNameSize = DNLEN + 1; + if (!LookupAccountSidW(nullptr, sid, accountName, + &accountNameSize, + domainName, &domainNameSize, &accountType)) + { + LOG_WARN(("Could not lookup account Sid, will try Users. (%d)", + GetLastError())); + wcsncpy(accountName, L"Users", UNLEN); + } + + // We already have the group name so we can get rid of the SID + FreeSid(sid); + sid = nullptr; + + // Build the ACE, BuildExplicitAccessWithName cannot fail so it is not logged. + EXPLICIT_ACCESS ea; + BuildExplicitAccessWithNameW(&ea, accountName, + SERVICE_START | SERVICE_STOP | GENERIC_READ, + SET_ACCESS, NO_INHERITANCE); + DWORD lastError = SetEntriesInAclW(1, (PEXPLICIT_ACCESS)&ea, pacl, &pNewAcl); + if (ERROR_SUCCESS != lastError) + { + LOG_WARN(("Could not set entries in ACL. (%d)", lastError)); + return lastError; + } + + // Initialize a new security descriptor. + SECURITY_DESCRIPTOR sd; + if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) + { + LOG_WARN(("Could not initialize security descriptor. (%d)", + GetLastError())); + return GetLastError(); + } + + // Set the new DACL in the security descriptor. + if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE)) + { + LOG_WARN(("Could not set security descriptor DACL. (%d)", + GetLastError())); + return GetLastError(); + } + + // Set the new security descriptor for the service object. + if (!SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, &sd)) + { + LOG_WARN(("Could not set object security. (%d)", + GetLastError())); + return GetLastError(); + } + + // Woohoo, raise the roof + LOG(("User access was set successfully on the service.")); + return ERROR_SUCCESS; } diff --git a/onlineupdate/source/service/serviceinstall.hxx b/onlineupdate/source/service/serviceinstall.hxx index d8532a968e42..1afaada84519 100644 --- a/onlineupdate/source/service/serviceinstall.hxx +++ b/onlineupdate/source/service/serviceinstall.hxx @@ -11,11 +11,11 @@ BOOL SvcInstall(SvcInstallAction action); BOOL SvcUninstall(); BOOL StopService(); BOOL SetUserAccessServiceDACL(SC_HANDLE hService); -DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, +DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, PSECURITY_DESCRIPTOR psd); struct MaintenanceServiceStringTable { - char serviceDescription[MAX_TEXT_LEN]; + char serviceDescription[MAX_TEXT_LEN]; }; diff --git a/onlineupdate/source/service/workmonitor.cxx b/onlineupdate/source/service/workmonitor.cxx index 76bbe1c911d7..05cf2481e7ce 100644 --- a/onlineupdate/source/service/workmonitor.cxx +++ b/onlineupdate/source/service/workmonitor.cxx @@ -45,38 +45,41 @@ BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath, static BOOL IsStatusApplying(LPCWSTR updateDirPath, BOOL &isApplying) { - isApplying = FALSE; - WCHAR updateStatusFilePath[MAX_PATH + 1] = {L'\0'}; - wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH); - if (!PathAppendSafe(updateStatusFilePath, L"update.status")) { - LOG_WARN(("Could not append path for update.status file")); - return FALSE; - } - - AutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_READ, + isApplying = FALSE; + WCHAR updateStatusFilePath[MAX_PATH + 1] = {L'\0'}; + wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH); + if (!PathAppendSafe(updateStatusFilePath, L"update.status")) + { + LOG_WARN(("Could not append path for update.status file")); + return FALSE; + } + + AutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr)); - if (statusFile == INVALID_HANDLE_VALUE) { - LOG_WARN(("Could not open update.status file")); - return FALSE; - } + if (statusFile == INVALID_HANDLE_VALUE) + { + LOG_WARN(("Could not open update.status file")); + return FALSE; + } - char buf[32] = { 0 }; - DWORD read; - if (!ReadFile(statusFile.get(), buf, sizeof(buf), &read, nullptr)) { - LOG_WARN(("Could not read from update.status file")); - return FALSE; - } + char buf[32] = { 0 }; + DWORD read; + if (!ReadFile(statusFile.get(), buf, sizeof(buf), &read, nullptr)) + { + LOG_WARN(("Could not read from update.status file")); + return FALSE; + } - LOG(("updater.exe returned status: %s", buf)); + LOG(("updater.exe returned status: %s", buf)); - const char kApplying[] = "applying"; - isApplying = strncmp(buf, kApplying, - sizeof(kApplying) - 1) == 0; - return TRUE; + const char kApplying[] = "applying"; + isApplying = strncmp(buf, kApplying, + sizeof(kApplying) - 1) == 0; + return TRUE; } /** @@ -89,9 +92,9 @@ IsStatusApplying(LPCWSTR updateDirPath, BOOL &isApplying) static bool IsUpdateBeingStaged(int argc, LPWSTR *argv) { - // PID will be set to -1 if we're supposed to stage an update. - return argc == 4 && !wcscmp(argv[3], L"-1") || - argc == 5 && !wcscmp(argv[4], L"-1"); + // PID will be set to -1 if we're supposed to stage an update. + return argc == 4 && !wcscmp(argv[3], L"-1") || + argc == 5 && !wcscmp(argv[4], L"-1"); } /** @@ -103,12 +106,14 @@ IsUpdateBeingStaged(int argc, LPWSTR *argv) static bool IsDigits(WCHAR *str) { - while (*str) { - if (!iswdigit(*str++)) { - return FALSE; + while (*str) + { + if (!iswdigit(*str++)) + { + return FALSE; + } } - } - return TRUE; + return TRUE; } /** @@ -124,8 +129,8 @@ IsDigits(WCHAR *str) static bool IsOldCommandline(int argc, LPWSTR *argv) { - return argc == 4 && !wcscmp(argv[3], L"-1") || - argc >= 4 && (wcsstr(argv[3], L"/replace") || IsDigits(argv[3])); + return argc == 4 && !wcscmp(argv[3], L"-1") || + argc >= 4 && (wcsstr(argv[3], L"/replace") || IsDigits(argv[3])); } /** @@ -138,31 +143,36 @@ IsOldCommandline(int argc, LPWSTR *argv) static BOOL GetInstallationDir(int argcTmp, LPWSTR *argvTmp, WCHAR aResultDir[MAX_PATH + 1]) { - int index = 3; - if (IsOldCommandline(argcTmp, argvTmp)) { - index = 2; - } - - if (argcTmp < index) { - return FALSE; - } - - wcsncpy(aResultDir, argvTmp[2], MAX_PATH); - WCHAR* backSlash = wcsrchr(aResultDir, L'\\'); - // Make sure that the path does not include trailing backslashes - if (backSlash && (backSlash[1] == L'\0')) { - *backSlash = L'\0'; - } - - // The new command line's argv[2] is always the installation directory. - if (index == 2) { - bool backgroundUpdate = IsUpdateBeingStaged(argcTmp, argvTmp); - bool replaceRequest = (argcTmp >= 4 && wcsstr(argvTmp[3], L"/replace")); - if (backgroundUpdate || replaceRequest) { - return PathRemoveFileSpecW(aResultDir); - } - } - return TRUE; + int index = 3; + if (IsOldCommandline(argcTmp, argvTmp)) + { + index = 2; + } + + if (argcTmp < index) + { + return FALSE; + } + + wcsncpy(aResultDir, argvTmp[2], MAX_PATH); + WCHAR* backSlash = wcsrchr(aResultDir, L'\\'); + // Make sure that the path does not include trailing backslashes + if (backSlash && (backSlash[1] == L'\0')) + { + *backSlash = L'\0'; + } + + // The new command line's argv[2] is always the installation directory. + if (index == 2) + { + bool backgroundUpdate = IsUpdateBeingStaged(argcTmp, argvTmp); + bool replaceRequest = (argcTmp >= 4 && wcsstr(argvTmp[3], L"/replace")); + if (backgroundUpdate || replaceRequest) + { + return PathRemoveFileSpecW(aResultDir); + } + } + return TRUE; } /** @@ -180,150 +190,172 @@ StartUpdateProcess(int argc, LPCWSTR installDir, BOOL &processStarted) { - LOG(("Starting update process as the service in session 0.")); - STARTUPINFO si = {0}; - si.cb = sizeof(STARTUPINFO); - si.lpDesktop = L"winsta0\\Default"; - PROCESS_INFORMATION pi = {0}; - - // The updater command line is of the form: - // updater.exe update-dir apply [wait-pid [callback-dir callback-path args]] - LPWSTR cmdLine = MakeCommandLine(argc, argv); - - int index = 3; - if (IsOldCommandline(argc, argv)) { - index = 2; - } - - // If we're about to start the update process from session 0, - // then we should not show a GUI. This only really needs to be done - // on Vista and higher, but it's better to keep everything consistent - // across all OS if it's of no harm. - if (argc >= index) { - // Setting the desktop to blank will ensure no GUI is displayed - si.lpDesktop = L""; - si.dwFlags |= STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; - } - - // We move the updater.ini file out of the way because we will handle - // executing PostUpdate through the service. We handle PostUpdate from - // the service because there are some per user things that happen that - // can't run in session 0 which we run updater.exe in. - // Once we are done running updater.exe we rename updater.ini back so - // that if there were any errors the next updater.exe will run correctly. - WCHAR updaterINI[MAX_PATH + 1]; - WCHAR updaterINITemp[MAX_PATH + 1]; - BOOL selfHandlePostUpdate = FALSE; - // We use the updater.ini from the same directory as the updater.exe - // because of background updates. - if (PathGetSiblingFilePath(updaterINI, argv[0], L"updater.ini") && - PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) { - selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp, - MOVEFILE_REPLACE_EXISTING); - } - - // Add an env var for USING_SERVICE so the updater.exe can - // do anything special that it needs to do for service updates. - // Search in updater.cpp for more info on USING_SERVICE. - putenv(const_cast<char*>("USING_SERVICE=1")); - LOG(("Starting service with cmdline: %ls", cmdLine)); - processStarted = CreateProcessW(argv[0], cmdLine, - nullptr, nullptr, FALSE, - CREATE_DEFAULT_ERROR_MODE, - nullptr, - nullptr, &si, &pi); - // Empty value on putenv is how you remove an env variable in Windows - putenv(const_cast<char*>("USING_SERVICE=")); - - BOOL updateWasSuccessful = FALSE; - if (processStarted) { - // Wait for the updater process to finish - LOG(("Process was started... waiting on result.")); - DWORD waitRes = WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER); - if (WAIT_TIMEOUT == waitRes) { - // We waited a long period of time for updater.exe and it never finished - // so kill it. - TerminateProcess(pi.hProcess, 1); - } else { - // Check the return code of updater.exe to make sure we get 0 - DWORD returnCode; - if (GetExitCodeProcess(pi.hProcess, &returnCode)) { - LOG(("Process finished with return code %d.", returnCode)); - // updater returns 0 if successful. - updateWasSuccessful = (returnCode == 0); - } else { - LOG_WARN(("Process finished but could not obtain return code.")); - } - } - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - // Check just in case updater.exe didn't change the status from - // applying. If this is the case we report an error. - BOOL isApplying = FALSE; - if (IsStatusApplying(argv[1], isApplying) && isApplying) { - if (updateWasSuccessful) { - LOG(("update.status is still applying even know update " - " was successful.")); - if (!WriteStatusFailure(argv[1], - SERVICE_STILL_APPLYING_ON_SUCCESS)) { - LOG_WARN(("Could not write update.status still applying on" - " success error.")); + LOG(("Starting update process as the service in session 0.")); + STARTUPINFO si = {0}; + si.cb = sizeof(STARTUPINFO); + si.lpDesktop = L"winsta0\\Default"; + PROCESS_INFORMATION pi = {0}; + + // The updater command line is of the form: + // updater.exe update-dir apply [wait-pid [callback-dir callback-path args]] + LPWSTR cmdLine = MakeCommandLine(argc, argv); + + int index = 3; + if (IsOldCommandline(argc, argv)) + { + index = 2; + } + + // If we're about to start the update process from session 0, + // then we should not show a GUI. This only really needs to be done + // on Vista and higher, but it's better to keep everything consistent + // across all OS if it's of no harm. + if (argc >= index) + { + // Setting the desktop to blank will ensure no GUI is displayed + si.lpDesktop = L""; + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + } + + // We move the updater.ini file out of the way because we will handle + // executing PostUpdate through the service. We handle PostUpdate from + // the service because there are some per user things that happen that + // can't run in session 0 which we run updater.exe in. + // Once we are done running updater.exe we rename updater.ini back so + // that if there were any errors the next updater.exe will run correctly. + WCHAR updaterINI[MAX_PATH + 1]; + WCHAR updaterINITemp[MAX_PATH + 1]; + BOOL selfHandlePostUpdate = FALSE; + // We use the updater.ini from the same directory as the updater.exe + // because of background updates. + if (PathGetSiblingFilePath(updaterINI, argv[0], L"updater.ini") && + PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) + { + selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp, + MOVEFILE_REPLACE_EXISTING); + } + + // Add an env var for USING_SERVICE so the updater.exe can + // do anything special that it needs to do for service updates. + // Search in updater.cpp for more info on USING_SERVICE. + putenv(const_cast<char*>("USING_SERVICE=1")); + LOG(("Starting service with cmdline: %ls", cmdLine)); + processStarted = CreateProcessW(argv[0], cmdLine, + nullptr, nullptr, FALSE, + CREATE_DEFAULT_ERROR_MODE, + nullptr, + nullptr, &si, &pi); + // Empty value on putenv is how you remove an env variable in Windows + putenv(const_cast<char*>("USING_SERVICE=")); + + BOOL updateWasSuccessful = FALSE; + if (processStarted) + { + // Wait for the updater process to finish + LOG(("Process was started... waiting on result.")); + DWORD waitRes = WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER); + if (WAIT_TIMEOUT == waitRes) + { + // We waited a long period of time for updater.exe and it never finished + // so kill it. + TerminateProcess(pi.hProcess, 1); } - // Since we still had applying we know updater.exe didn't do its - // job correctly. - updateWasSuccessful = FALSE; - } else { - LOG_WARN(("update.status is still applying and update was not successful.")); - if (!WriteStatusFailure(argv[1], - SERVICE_STILL_APPLYING_ON_FAILURE)) { - LOG_WARN(("Could not write update.status still applying on" - " success error.")); + else + { + // Check the return code of updater.exe to make sure we get 0 + DWORD returnCode; + if (GetExitCodeProcess(pi.hProcess, &returnCode)) + { + LOG(("Process finished with return code %d.", returnCode)); + // updater returns 0 if successful. + updateWasSuccessful = (returnCode == 0); + } + else + { + LOG_WARN(("Process finished but could not obtain return code.")); + } + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + // Check just in case updater.exe didn't change the status from + // applying. If this is the case we report an error. + BOOL isApplying = FALSE; + if (IsStatusApplying(argv[1], isApplying) && isApplying) + { + if (updateWasSuccessful) + { + LOG(("update.status is still applying even know update " + " was successful.")); + if (!WriteStatusFailure(argv[1], + SERVICE_STILL_APPLYING_ON_SUCCESS)) + { + LOG_WARN(("Could not write update.status still applying on" + " success error.")); + } + // Since we still had applying we know updater.exe didn't do its + // job correctly. + updateWasSuccessful = FALSE; + } + else + { + LOG_WARN(("update.status is still applying and update was not successful.")); + if (!WriteStatusFailure(argv[1], + SERVICE_STILL_APPLYING_ON_FAILURE)) + { + LOG_WARN(("Could not write update.status still applying on" + " success error.")); + } + } } - } - } - } else { - DWORD lastError = GetLastError(); - LOG_WARN(("Could not create process as current user, " - "updaterPath: %ls; cmdLine: %ls. (%d)", - argv[0], cmdLine, lastError)); - } - - // Now that we're done with the update, restore back the updater.ini file - // We use it ourselves, and also we want it back in case we had any type - // of error so that the normal update process can use it. - if (selfHandlePostUpdate) { - MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING); - - // Only run the PostUpdate if the update was successful - if (updateWasSuccessful && argc > index) { - LPCWSTR updateInfoDir = argv[1]; - bool stagingUpdate = IsUpdateBeingStaged(argc, argv); - - // Launch the PostProcess with admin access in session 0. This is - // actually launching the post update process but it takes in the - // callback app path to figure out where to apply to. - // The PostUpdate process with user only access will be done inside - // the unelevated updater.exe after the update process is complete - // from the service. We don't know here which session to start - // the user PostUpdate process from. - // Note that we don't need to do this if we're just staging the - // update in the background, as the PostUpdate step runs when - // performing the replacing in that case. - if (!stagingUpdate) { - LOG(("Launching post update process as the service in session 0.")); - if (!LaunchWinPostProcess(installDir, updateInfoDir, true, nullptr)) { - LOG_WARN(("The post update process could not be launched." - " installDir: %ls, updateInfoDir: %ls", - installDir, updateInfoDir)); + } + else + { + DWORD lastError = GetLastError(); + LOG_WARN(("Could not create process as current user, " + "updaterPath: %ls; cmdLine: %ls. (%d)", + argv[0], cmdLine, lastError)); + } + + // Now that we're done with the update, restore back the updater.ini file + // We use it ourselves, and also we want it back in case we had any type + // of error so that the normal update process can use it. + if (selfHandlePostUpdate) + { + MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING); + + // Only run the PostUpdate if the update was successful + if (updateWasSuccessful && argc > index) + { + LPCWSTR updateInfoDir = argv[1]; + bool stagingUpdate = IsUpdateBeingStaged(argc, argv); + + // Launch the PostProcess with admin access in session 0. This is + // actually launching the post update process but it takes in the + // callback app path to figure out where to apply to. + // The PostUpdate process with user only access will be done inside + // the unelevated updater.exe after the update process is complete + // from the service. We don't know here which session to start + // the user PostUpdate process from. + // Note that we don't need to do this if we're just staging the + // update in the background, as the PostUpdate step runs when + // performing the replacing in that case. + if (!stagingUpdate) + { + LOG(("Launching post update process as the service in session 0.")); + if (!LaunchWinPostProcess(installDir, updateInfoDir, true, nullptr)) + { + LOG_WARN(("The post update process could not be launched." + " installDir: %ls, updateInfoDir: %ls", + installDir, updateInfoDir)); + } + } } - } } - } - free(cmdLine); - return updateWasSuccessful; + free(cmdLine); + return updateWasSuccessful; } /** @@ -337,185 +369,219 @@ StartUpdateProcess(int argc, BOOL ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv) { - BOOL result = TRUE; - if (argc < 3) { - LOG_WARN(("Not enough command line parameters specified. " - "Updating update.status.")); - - // We can only update update.status if argv[1] exists. argv[1] is - // the directory where the update.status file exists. - if (argc < 2 || - !WriteStatusFailure(argv[1], - SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) { - LOG_WARN(("Could not write update.status service update failure. (%d)", - GetLastError())); - } - return FALSE; - } - - WCHAR installDir[MAX_PATH + 1] = {L'\0'}; - if (!GetInstallationDir(argc, argv, installDir)) { - LOG_WARN(("Could not get the installation directory")); - if (!WriteStatusFailure(argv[1], - SERVICE_INSTALLDIR_ERROR)) { - LOG_WARN(("Could not write update.status for GetInstallationDir failure.")); - } - return FALSE; - } - - // Make sure the path to the updater to use for the update is local. - // We do this check to make sure that file locking is available for - // race condition security checks. - BOOL isLocal = FALSE; - if (!IsLocalFile(argv[0], isLocal) || !isLocal) { - LOG_WARN(("Filesystem in path %ls is not supported (%d)", - argv[0], GetLastError())); - if (!WriteStatusFailure(argv[1], - SERVICE_UPDATER_NOT_FIXED_DRIVE)) { - LOG_WARN(("Could not write update.status service update failure. (%d)", - GetLastError())); - } - return FALSE; - } - - AutoHandle noWriteLock(CreateFileW(argv[0], GENERIC_READ, FILE_SHARE_READ, + BOOL result = TRUE; + if (argc < 3) + { + LOG_WARN(("Not enough command line parameters specified. " + "Updating update.status.")); + + // We can only update update.status if argv[1] exists. argv[1] is + // the directory where the update.status file exists. + if (argc < 2 || + !WriteStatusFailure(argv[1], + SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) + { + LOG_WARN(("Could not write update.status service update failure. (%d)", + GetLastError())); + } + return FALSE; + } + + WCHAR installDir[MAX_PATH + 1] = {L'\0'}; + if (!GetInstallationDir(argc, argv, installDir)) + { + LOG_WARN(("Could not get the installation directory")); + if (!WriteStatusFailure(argv[1], + SERVICE_INSTALLDIR_ERROR)) + { + LOG_WARN(("Could not write update.status for GetInstallationDir failure.")); + } + return FALSE; + } + + // Make sure the path to the updater to use for the update is local. + // We do this check to make sure that file locking is available for + // race condition security checks. + BOOL isLocal = FALSE; + if (!IsLocalFile(argv[0], isLocal) || !isLocal) + { + LOG_WARN(("Filesystem in path %ls is not supported (%d)", + argv[0], GetLastError())); + if (!WriteStatusFailure(argv[1], + SERVICE_UPDATER_NOT_FIXED_DRIVE)) + { + LOG_WARN(("Could not write update.status service update failure. (%d)", + GetLastError())); + } + return FALSE; + } + + AutoHandle noWriteLock(CreateFileW(argv[0], GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)); - if (noWriteLock == INVALID_HANDLE_VALUE) { - LOG_WARN(("Could not set no write sharing access on file. (%d)", - GetLastError())); - if (!WriteStatusFailure(argv[1], - SERVICE_COULD_NOT_LOCK_UPDATER)) { - LOG_WARN(("Could not write update.status service update failure. (%d)", - GetLastError())); - } - return FALSE; - } - - // Verify that the updater.exe that we are executing is the same - // as the one in the installation directory which we are updating. - // The installation dir that we are installing to is installDir. - WCHAR installDirUpdater[MAX_PATH + 1] = { L'\0' }; - wcsncpy(installDirUpdater, installDir, MAX_PATH); - if (!PathAppendSafe(installDirUpdater, L"updater.exe")) { - LOG_WARN(("Install directory updater could not be determined.")); - result = FALSE; - } - - BOOL updaterIsCorrect = FALSE; - if (result && !VerifySameFiles(argv[0], installDirUpdater, - updaterIsCorrect)) { - LOG_WARN(("Error checking if the updaters are the same.\n" - "Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater)); - result = FALSE; - } - - if (result && !updaterIsCorrect) { - LOG_WARN(("The updaters do not match, updater will not run.\n" - "Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater)); - result = FALSE; - } - - if (result) { - LOG(("updater.exe was compared successfully to the installation directory" - " updater.exe.")); - } else { - if (!WriteStatusFailure(argv[1], - SERVICE_UPDATER_COMPARE_ERROR)) { - LOG_WARN(("Could not write update.status updater compare failure.")); - } - return FALSE; - } - - // Check to make sure the updater.exe module has the unique updater identity. - // This is a security measure to make sure that the signed executable that - // we will run is actually an updater. - HMODULE updaterModule = LoadLibraryEx(argv[0], nullptr, - LOAD_LIBRARY_AS_DATAFILE); - if (!updaterModule) { - LOG_WARN(("updater.exe module could not be loaded. (%d)", GetLastError())); - result = FALSE; - } else { - char updaterIdentity[64]; - if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY, - updaterIdentity, sizeof(updaterIdentity))) { - LOG_WARN(("The updater.exe application does not contain the Mozilla" - " updater identity.")); - result = FALSE; - } - - if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) { - LOG_WARN(("The updater.exe identity string is not valid.")); - result = FALSE; - } - FreeLibrary(updaterModule); - } - - if (result) { - LOG(("The updater.exe application contains the Mozilla" - " updater identity.")); - } else { - if (!WriteStatusFailure(argv[1], - SERVICE_UPDATER_IDENTITY_ERROR)) { - LOG_WARN(("Could not write update.status no updater identity.")); + if (noWriteLock == INVALID_HANDLE_VALUE) + { + LOG_WARN(("Could not set no write sharing access on file. (%d)", + GetLastError())); + if (!WriteStatusFailure(argv[1], + SERVICE_COULD_NOT_LOCK_UPDATER)) + { + LOG_WARN(("Could not write update.status service update failure. (%d)", + GetLastError())); + } + return FALSE; } - return TRUE; - } - // Check for updater.exe sign problems - BOOL updaterSignProblem = FALSE; -#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK - updaterSignProblem = !DoesBinaryMatchAllowedCertificates(installDir, - argv[0]); -#endif + // Verify that the updater.exe that we are executing is the same + // as the one in the installation directory which we are updating. + // The installation dir that we are installing to is installDir. + WCHAR installDirUpdater[MAX_PATH + 1] = { L'\0' }; + wcsncpy(installDirUpdater, installDir, MAX_PATH); + if (!PathAppendSafe(installDirUpdater, L"updater.exe")) + { + LOG_WARN(("Install directory updater could not be determined.")); + result = FALSE; + } + + BOOL updaterIsCorrect = FALSE; + if (result && !VerifySameFiles(argv[0], installDirUpdater, + updaterIsCorrect)) + { + LOG_WARN(("Error checking if the updaters are the same.\n" + "Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater)); + result = FALSE; + } + + if (result && !updaterIsCorrect) + { + LOG_WARN(("The updaters do not match, updater will not run.\n" + "Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater)); + result = FALSE; + } - // Only proceed with the update if we have no signing problems - if (!updaterSignProblem) { - BOOL updateProcessWasStarted = FALSE; - if (StartUpdateProcess(argc, argv, installDir, - updateProcessWasStarted)) { - LOG(("updater.exe was launched and run successfully!")); - LogFlush(); - - // Don't attempt to update the service when the update is being staged. - if (!IsUpdateBeingStaged(argc, argv)) { - // We might not execute code after StartServiceUpdate because - // the service installer will stop the service if it is running. - StartServiceUpdate(installDir); - } - } else { - result = FALSE; - LOG_WARN(("Error running update process. Updating update.status (%d)", - GetLastError())); - LogFlush(); - - // If the update process was started, then updater.exe is responsible for - // setting the failure code. If it could not be started then we do the - // work. We set an error instead of directly setting status pending - // so that the app.update.service.errors pref can be updated when - // the callback app restarts. - if (!updateProcessWasStarted) { + if (result) + { + LOG(("updater.exe was compared successfully to the installation directory" + " updater.exe.")); + } + else + { if (!WriteStatusFailure(argv[1], - SERVICE_UPDATER_COULD_NOT_BE_STARTED)) { - LOG_WARN(("Could not write update.status service update failure. (%d)", - GetLastError())); + SERVICE_UPDATER_COMPARE_ERROR)) + { + LOG_WARN(("Could not write update.status updater compare failure.")); + } + return FALSE; + } + + // Check to make sure the updater.exe module has the unique updater identity. + // This is a security measure to make sure that the signed executable that + // we will run is actually an updater. + HMODULE updaterModule = LoadLibraryEx(argv[0], nullptr, + LOAD_LIBRARY_AS_DATAFILE); + if (!updaterModule) + { + LOG_WARN(("updater.exe module could not be loaded. (%d)", GetLastError())); + result = FALSE; + } + else + { + char updaterIdentity[64]; + if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY, + updaterIdentity, sizeof(updaterIdentity))) + { + LOG_WARN(("The updater.exe application does not contain the Mozilla" + " updater identity.")); + result = FALSE; + } + + if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) + { + LOG_WARN(("The updater.exe identity string is not valid.")); + result = FALSE; } - } + FreeLibrary(updaterModule); } - } else { - result = FALSE; - LOG_WARN(("Could not start process due to certificate check error on " - "updater.exe. Updating update.status. (%d)", GetLastError())); - // When there is a certificate check error on the updater.exe application, - // we want to write out the error. - if (!WriteStatusFailure(argv[1], - SERVICE_UPDATER_SIGN_ERROR)) { - LOG_WARN(("Could not write pending state to update.status. (%d)", - GetLastError())); + if (result) + { + LOG(("The updater.exe application contains the Mozilla" + " updater identity.")); + } + else + { + if (!WriteStatusFailure(argv[1], + SERVICE_UPDATER_IDENTITY_ERROR)) + { + LOG_WARN(("Could not write update.status no updater identity.")); + } + return TRUE; + } + + // Check for updater.exe sign problems + BOOL updaterSignProblem = FALSE; +#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK + updaterSignProblem = !DoesBinaryMatchAllowedCertificates(installDir, + argv[0]); +#endif + + // Only proceed with the update if we have no signing problems + if (!updaterSignProblem) + { + BOOL updateProcessWasStarted = FALSE; + if (StartUpdateProcess(argc, argv, installDir, + updateProcessWasStarted)) + { + LOG(("updater.exe was launched and run successfully!")); + LogFlush(); + + // Don't attempt to update the service when the update is being staged. + if (!IsUpdateBeingStaged(argc, argv)) + { + // We might not execute code after StartServiceUpdate because + // the service installer will stop the service if it is running. + StartServiceUpdate(installDir); + } + } + else + { + result = FALSE; + LOG_WARN(("Error running update process. Updating update.status (%d)", + GetLastError())); + LogFlush(); + + // If the update process was started, then updater.exe is responsible for + // setting the failure code. If it could not be started then we do the + // work. We set an error instead of directly setting status pending + // so that the app.update.service.errors pref can be updated when + // the callback app restarts. + if (!updateProcessWasStarted) + { + if (!WriteStatusFailure(argv[1], + SERVICE_UPDATER_COULD_NOT_BE_STARTED)) + { + LOG_WARN(("Could not write update.status service update failure. (%d)", + GetLastError())); + } + } + } + } + else + { + result = FALSE; + LOG_WARN(("Could not start process due to certificate check error on " + "updater.exe. Updating update.status. (%d)", GetLastError())); + + // When there is a certificate check error on the updater.exe application, + // we want to write out the error. + if (!WriteStatusFailure(argv[1], + SERVICE_UPDATER_SIGN_ERROR)) + { + LOG_WARN(("Could not write pending state to update.status. (%d)", + GetLastError())); + } } - } - return result; + return result; } /** @@ -530,33 +596,37 @@ ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv) BOOL GetSecureUpdaterPath(WCHAR serviceUpdaterPath[MAX_PATH + 1]) { - if (!GetModuleFileNameW(nullptr, serviceUpdaterPath, MAX_PATH)) { - LOG_WARN(("Could not obtain module filename when attempting to " - "use a secure updater path. (%d)", GetLastError())); - return FALSE; - } - - if (!PathRemoveFileSpecW(serviceUpdaterPath)) { - LOG_WARN(("Couldn't remove file spec when attempting to use a secure " - "updater path. (%d)", GetLastError())); - return FALSE; - } - - if (!PathAppendSafe(serviceUpdaterPath, L"update")) { - LOG_WARN(("Couldn't append file spec when attempting to use a secure " - "updater path. (%d)", GetLastError())); - return FALSE; - } - - CreateDirectoryW(serviceUpdaterPath, nullptr); - - if (!PathAppendSafe(serviceUpdaterPath, L"updater.exe")) { - LOG_WARN(("Couldn't append file spec when attempting to use a secure " - "updater path. (%d)", GetLastError())); - return FALSE; - } - - return TRUE; + if (!GetModuleFileNameW(nullptr, serviceUpdaterPath, MAX_PATH)) + { + LOG_WARN(("Could not obtain module filename when attempting to " + "use a secure updater path. (%d)", GetLastError())); + return FALSE; + } + + if (!PathRemoveFileSpecW(serviceUpdaterPath)) + { + LOG_WARN(("Couldn't remove file spec when attempting to use a secure " + "updater path. (%d)", GetLastError())); + return FALSE; + } + + if (!PathAppendSafe(serviceUpdaterPath, L"update")) + { + LOG_WARN(("Couldn't append file spec when attempting to use a secure " + "updater path. (%d)", GetLastError())); + return FALSE; + } + + CreateDirectoryW(serviceUpdaterPath, nullptr); + + if (!PathAppendSafe(serviceUpdaterPath, L"updater.exe")) + { + LOG_WARN(("Couldn't append file spec when attempting to use a secure " + "updater path. (%d)", GetLastError())); + return FALSE; + } + + return TRUE; } /** @@ -568,27 +638,31 @@ GetSecureUpdaterPath(WCHAR serviceUpdaterPath[MAX_PATH + 1]) BOOL DeleteSecureUpdater(WCHAR serviceUpdaterPath[MAX_PATH + 1]) { - BOOL result = FALSE; - if (serviceUpdaterPath[0]) { - result = DeleteFileW(serviceUpdaterPath); - if (!result && GetLastError() != ERROR_PATH_NOT_FOUND && - GetLastError() != ERROR_FILE_NOT_FOUND) { - LOG_WARN(("Could not delete service updater path: '%ls'.", - serviceUpdaterPath)); - } - - WCHAR updaterINIPath[MAX_PATH + 1] = { L'\0' }; - if (PathGetSiblingFilePath(updaterINIPath, serviceUpdaterPath, - L"updater.ini")) { - result = DeleteFileW(updaterINIPath); - if (!result && GetLastError() != ERROR_PATH_NOT_FOUND && - GetLastError() != ERROR_FILE_NOT_FOUND) { - LOG_WARN(("Could not delete service updater INI path: '%ls'.", - updaterINIPath)); - } - } - } - return result; + BOOL result = FALSE; + if (serviceUpdaterPath[0]) + { + result = DeleteFileW(serviceUpdaterPath); + if (!result && GetLastError() != ERROR_PATH_NOT_FOUND && + GetLastError() != ERROR_FILE_NOT_FOUND) + { + LOG_WARN(("Could not delete service updater path: '%ls'.", + serviceUpdaterPath)); + } + + WCHAR updaterINIPath[MAX_PATH + 1] = { L'\0' }; + if (PathGetSiblingFilePath(updaterINIPath, serviceUpdaterPath, + L"updater.ini")) + { + result = DeleteFileW(updaterINIPath); + if (!result && GetLastError() != ERROR_PATH_NOT_FOUND && + GetLastError() != ERROR_FILE_NOT_FOUND) + { + LOG_WARN(("Could not delete service updater INI path: '%ls'.", + updaterINIPath)); + } + } + } + return result; } /** @@ -604,81 +678,93 @@ DeleteSecureUpdater(WCHAR serviceUpdaterPath[MAX_PATH + 1]) BOOL ExecuteServiceCommand(int argc, LPWSTR *argv) { - if (argc < 3) { - LOG_WARN(("Not enough command line arguments to execute a service command")); - return FALSE; - } - - // The tests work by making sure the log has changed, so we put a - // unique ID in the log. - RPC_WSTR guidString = RPC_WSTR(L""); - GUID guid; - HRESULT hr = CoCreateGuid(&guid); - if (SUCCEEDED(hr)) { - UuidToString(&guid, &guidString); - } - LOG(("Executing service command %ls, ID: %ls", - argv[2], reinterpret_cast<LPCWSTR>(guidString))); - RpcStringFree(&guidString); - - BOOL result = FALSE; - if (!lstrcmpi(argv[2], L"software-update")) { - - // Use the passed in command line arguments for the update, except for the - // path to updater.exe. We copy updater.exe to a the directory of the - // MozillaMaintenance service so that a low integrity process cannot - // replace the updater.exe at any point and use that for the update. - // It also makes DLL injection attacks harder. - LPWSTR oldUpdaterPath = argv[3]; - WCHAR secureUpdaterPath[MAX_PATH + 1] = { L'\0' }; - result = GetSecureUpdaterPath(secureUpdaterPath); // Does its own logging - if (result) { - LOG(("Passed in path: '%ls'; Using this path for updating: '%ls'.", - oldUpdaterPath, secureUpdaterPath)); - DeleteSecureUpdater(secureUpdaterPath); - result = CopyFileW(oldUpdaterPath, secureUpdaterPath, FALSE); - } - - if (!result) { - LOG_WARN(("Could not copy path to secure location. (%d)", - GetLastError())); - if (argc > 4 && !WriteStatusFailure(argv[4], - SERVICE_COULD_NOT_COPY_UPDATER)) { - LOG_WARN(("Could not write update.status could not copy updater error")); - } - } else { - - // We obtained the path and copied it successfully, update the path to - // use for the service update. - argv[3] = secureUpdaterPath; - - WCHAR oldUpdaterINIPath[MAX_PATH + 1] = { L'\0' }; - WCHAR secureUpdaterINIPath[MAX_PATH + 1] = { L'\0' }; - if (PathGetSiblingFilePath(secureUpdaterINIPath, secureUpdaterPath, - L"updater.ini") && - PathGetSiblingFilePath(oldUpdaterINIPath, oldUpdaterPath, - L"updater.ini")) { - // This is non fatal if it fails there is no real harm - if (!CopyFileW(oldUpdaterINIPath, secureUpdaterINIPath, FALSE)) { - LOG_WARN(("Could not copy updater.ini from: '%ls' to '%ls'. (%d)", - oldUpdaterINIPath, secureUpdaterINIPath, GetLastError())); - } - } + if (argc < 3) + { + LOG_WARN(("Not enough command line arguments to execute a service command")); + return FALSE; + } - result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3); - DeleteSecureUpdater(secureUpdaterPath); + // The tests work by making sure the log has changed, so we put a + // unique ID in the log. + RPC_WSTR guidString = RPC_WSTR(L""); + GUID guid; + HRESULT hr = CoCreateGuid(&guid); + if (SUCCEEDED(hr)) + { + UuidToString(&guid, &guidString); } + LOG(("Executing service command %ls, ID: %ls", + argv[2], reinterpret_cast<LPCWSTR>(guidString))); + RpcStringFree(&guidString); + + BOOL result = FALSE; + if (!lstrcmpi(argv[2], L"software-update")) + { + + // Use the passed in command line arguments for the update, except for the + // path to updater.exe. We copy updater.exe to a the directory of the + // MozillaMaintenance service so that a low integrity process cannot + // replace the updater.exe at any point and use that for the update. + // It also makes DLL injection attacks harder. + LPWSTR oldUpdaterPath = argv[3]; + WCHAR secureUpdaterPath[MAX_PATH + 1] = { L'\0' }; + result = GetSecureUpdaterPath(secureUpdaterPath); // Does its own logging + if (result) + { + LOG(("Passed in path: '%ls'; Using this path for updating: '%ls'.", + oldUpdaterPath, secureUpdaterPath)); + DeleteSecureUpdater(secureUpdaterPath); + result = CopyFileW(oldUpdaterPath, secureUpdaterPath, FALSE); + } - // We might not reach here if the service install succeeded - // because the service self updates itself and the service - // installer will stop the service. - LOG(("Service command %ls complete.", argv[2])); - } else { - LOG_WARN(("Service command not recognized: %ls.", argv[2])); - // result is already set to FALSE - } + if (!result) + { + LOG_WARN(("Could not copy path to secure location. (%d)", + GetLastError())); + if (argc > 4 && !WriteStatusFailure(argv[4], + SERVICE_COULD_NOT_COPY_UPDATER)) + { + LOG_WARN(("Could not write update.status could not copy updater error")); + } + } + else + { + + // We obtained the path and copied it successfully, update the path to + // use for the service update. + argv[3] = secureUpdaterPath; + + WCHAR oldUpdaterINIPath[MAX_PATH + 1] = { L'\0' }; + WCHAR secureUpdaterINIPath[MAX_PATH + 1] = { L'\0' }; + if (PathGetSiblingFilePath(secureUpdaterINIPath, secureUpdaterPath, + L"updater.ini") && + PathGetSiblingFilePath(oldUpdaterINIPath, oldUpdaterPath, + L"updater.ini")) + { + // This is non fatal if it fails there is no real harm + if (!CopyFileW(oldUpdaterINIPath, secureUpdaterINIPath, FALSE)) + { + LOG_WARN(("Could not copy updater.ini from: '%ls' to '%ls'. (%d)", + oldUpdaterINIPath, secureUpdaterINIPath, GetLastError())); + } + } + + result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3); + DeleteSecureUpdater(secureUpdaterPath); + } - LOG(("service command %ls complete with result: %ls.", - argv[1], (result ? L"Success" : L"Failure"))); - return TRUE; + // We might not reach here if the service install succeeded + // because the service self updates itself and the service + // installer will stop the service. + LOG(("Service command %ls complete.", argv[2])); + } + else + { + LOG_WARN(("Service command not recognized: %ls.", argv[2])); + // result is already set to FALSE + } + + LOG(("service command %ls complete with result: %ls.", + argv[1], (result ? L"Success" : L"Failure"))); + return TRUE; } |