void Service::install() { // Open service manager SC_HANDLE pSCM = OpenSCManagerW( nullptr, nullptr, SC_MANAGER_CREATE_SERVICE ); if ( !pSCM ) EXCEPT_WINAPI( L"Couldn't open service manager" ); // Fetch & escape path, add argument WCHAR wszPath[MAX_PATH]; if ( !GetModuleFileNameW( nullptr, wszPath, MAX_PATH ) ) EXCEPT_WINAPI( L"Couldn't get module file name" ); PathQuoteSpacesW( wszPath ); wcscat_s( wszPath, MAX_PATH, L" -s" ); // Create service SC_HANDLE hService = CreateServiceW( pSCM, mName.c_str(), mDisplayName.c_str(), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, wszPath, nullptr, nullptr, nullptr, L"NT AUTHORITY\\NetworkService", nullptr ); if ( !hService ) EXCEPT_WINAPI( L"Couldn't create service" ); // Set description SERVICE_DESCRIPTIONW descriptor; descriptor.lpDescription = new WCHAR[mDescription.length()]; wcscpy_s( descriptor.lpDescription, mDescription.length(), mDescription.c_str() ); ChangeServiceConfig2W( hService, SERVICE_CONFIG_DESCRIPTION, &descriptor ); delete[] descriptor.lpDescription; // Done CloseServiceHandle( hService ); CloseServiceHandle( pSCM ); }
/************************************************************************* * PathQuoteSpaces [SHELL32.55] */ VOID WINAPI PathQuoteSpacesAW (LPVOID lpszPath) { if(SHELL_OsIsUnicode()) PathQuoteSpacesW(lpszPath); else PathQuoteSpacesA(lpszPath); }
/** * Determines if the MozillaMaintenance service path needs to be updated * and fixes it if it is wrong. * * @param service A handle to the service to fix. * @param currentServicePath The current (possibly wrong) path that is used. * @param servicePathWasWrong Out parameter set to TRUE if a fix was needed. * @return TRUE if the service path is now correct. */ BOOL 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; 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; }
bool LSAPIInit::setShellFolderVariable(LPCWSTR pwzVariable, int nFolder) { bool bSuccess = false; wchar_t wzPath[MAX_PATH] = { 0 }; if (GetShellFolderPath(nFolder, wzPath, MAX_PATH)) { PathAddBackslashEx(wzPath, MAX_PATH); PathQuoteSpacesW(wzPath); m_smSettingsManager->SetVariable(pwzVariable, wzPath); bSuccess = true; } return bSuccess; }
/** * Determines if the MozillaMaintenance service path needs to be updated * and fixes it if it is wrong. * * @param service A handle to the service to fix. * @param currentServicePath The current (possibly wrong) path that is used. * @param servicePathWasWrong Out parameter set to TRUE if a fix was needed. * @return TRUE if the service path is now correct. */ BOOL 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. bool doesServiceHaveCorrectPath = !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe"); if (doesServiceHaveCorrectPath) { LOG(("The MozillaMaintenance service path is correct.\n")); servicePathWasWrong = FALSE; return TRUE; } LOG(("The MozillaMaintenance path is NOT correct.\n")); servicePathWasWrong = TRUE; WCHAR fixedPath[MAX_PATH + 1] = { L'\0' }; wcsncpy(fixedPath, currentServicePath, MAX_PATH); PathUnquoteSpacesW(fixedPath); if (!PathRemoveFileSpecW(fixedPath)) { LOG(("Couldn't remove file spec. (%d)\n", GetLastError())); return FALSE; } if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) { LOG(("Couldn't append file spec. (%d)\n", GetLastError())); return FALSE; } PathQuoteSpacesW(fixedPath); if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, fixedPath, NULL, NULL, NULL, NULL, NULL, NULL)) { LOG(("Could not fix service path. (%d)\n", GetLastError())); return FALSE; } LOG(("Fixed service path to: %ls.\n", fixedPath)); return TRUE; }
/** * Installs or upgrades the SVC_NAME service. * If an existing service is already installed, we replace it with the * currently running process. * * @param action The action to perform. * @return TRUE if the service was installed/upgraded */ BOOL SvcInstall(SvcInstallAction action) { // Get a handle to the local computer SCM database with full access rights. nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); if (!schSCManager) { LOG(("Could not open service manager. (%d)\n", GetLastError())); return FALSE; } WCHAR newServiceBinaryPath[MAX_PATH + 1]; if (!GetModuleFileNameW(NULL, newServiceBinaryPath, sizeof(newServiceBinaryPath) / sizeof(newServiceBinaryPath[0]))) { LOG(("Could not obtain module filename when attempting to " "install service. (%d)\n", GetLastError())); return FALSE; } // Check if we already have the service installed. nsAutoServiceHandle schService(OpenServiceW(schSCManager, 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(("Could not open service. (%d)\n", 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)) { LOG(("Could not reset security ACE on service handle. It might not be " "possible to start the service. This error should never " "happen. (%d)\n", GetLastError())); } // The service exists and we opened it DWORD bytesNeeded; if (!QueryServiceConfigW(schService, NULL, 0, &bytesNeeded) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { LOG(("Could not determine buffer size for query service config. (%d)\n", GetLastError())); return FALSE; } // Get the service config information, in particular we want the binary // path of the service. nsAutoArrayPtr<char> serviceConfigBuffer = new char[bytesNeeded]; if (!QueryServiceConfigW(schService, reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), bytesNeeded, &bytesNeeded)) { LOG(("Could open service but could not query service config. (%d)\n", 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, serviceConfig.lpBinaryPathName, servicePathWasWrong)) { LOG(("Could not fix service path. This should never happen. (%d)\n", 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\n", 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(("Could not obtain version number from new path\n")); 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); 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\"\n", 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(("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)\n", 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(("ERROR: The new service binary could not be copied in." " The service will not be upgraded.\n")); result = FALSE; } else { LOG(("The new service binary was copied in by first moving the" " old one out of the way.\n")); } // Attempt to get rid of the old service temp path. if (DeleteFileW(oldServiceBinaryTempPath)) { LOG(("The old temp service path was deleted: %ls.\n", 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(("WARNING: The old temp service path was not deleted.\n")); } } else { // It is best to leave the old service binary in this condition. LOG(("ERROR: Could not move old service file out of the way from:" " \"%ls\" to \"%ls\". Service will not be upgraded. (%d)\n", serviceConfig.lpBinaryPathName, oldServiceBinaryTempPath, GetLastError())); result = FALSE; } delete[] oldServiceBinaryTempPath; } else { // It is best to leave the old service binary in this condition. LOG(("ERROR: Service binary path was less than 3, service will" " not be updated. This should never happen.\n")); result = FALSE; } } else { LOG(("The new service binary was copied in.\n")); } // 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, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) { LOG(("Deleting the old file path on the next reboot: %ls.\n", newServiceBinaryPath)); } else { LOG(("Call to delete the old file path failed: %ls.\n", 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, NULL, 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.own(CreateServiceW(schSCManager, SVC_NAME, SVC_DISPLAY_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, newServiceBinaryPath, NULL, NULL, NULL, NULL, NULL)); if (!schService) { LOG(("Could not create Windows service. " "This error should never happen since a service install " "should only be called when elevated. (%d)\n", GetLastError())); return FALSE; } if (!SetUserAccessServiceDACL(schService)) { LOG(("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)\n", GetLastError())); } UpdateServiceDescription(schService); return TRUE; }
void LSAPIInit::setLitestepVars() { wchar_t wzTemp[MAX_PATH]; DWORD dwLength = MAX_PATH; // just using a shorter name, no real reason to re-assign. SettingsManager *pSM = m_smSettingsManager; // Set the variable "litestepdir" since it was never set if (SUCCEEDED(StringCchCopyW(wzTemp, MAX_PATH, m_wzLitestepPath))) { PathAddBackslashEx(wzTemp, MAX_PATH); PathQuoteSpacesW(wzTemp); pSM->SetVariable(L"litestepdir", wzTemp); } if (GetWindowsDirectoryW(wzTemp, MAX_PATH)) { PathAddBackslashEx(wzTemp, MAX_PATH); pSM->SetVariable(L"windir", wzTemp); } if (GetUserNameW(wzTemp, &dwLength)) { PathQuoteSpacesW(wzTemp); pSM->SetVariable(L"username", wzTemp); } pSM->SetVariable(L"nl", L"\n", true); pSM->SetVariable(L"cr", L"\r", true); pSM->SetVariable(L"dollar", L"$", true); pSM->SetVariable(L"at", L"@", true); pSM->SetVariable(L"bitbucket", L"::{645FF040-5081-101B-9F08-00AA002F954E}"); pSM->SetVariable(L"documents", L"::{450D8FBA-AD25-11D0-98A8-0800361B1103}"); pSM->SetVariable(L"drives", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); pSM->SetVariable(L"network", L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}"); pSM->SetVariable(L"controls", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); pSM->SetVariable(L"dialup", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{992CFFA0-F557-101A-88EC-00DD010CCC48}"); pSM->SetVariable(L"networkanddialup", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}"); pSM->SetVariable(L"printers", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"); pSM->SetVariable(L"scheduled", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{D6277990-4C6A-11CF-8D87-00AA0060F5BF}"); pSM->SetVariable(L"admintools", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{D20EA4E1-3957-11d2-A40B-0C5020524153}"); setShellFolderVariable(L"quicklaunch", LS_CSIDL_QUICKLAUNCH); setShellFolderVariable(L"commondesktopdir", CSIDL_COMMON_DESKTOPDIRECTORY); setShellFolderVariable(L"commonfavorites", CSIDL_COMMON_FAVORITES); setShellFolderVariable(L"commonprograms", CSIDL_COMMON_PROGRAMS); setShellFolderVariable(L"commonstartmenu", CSIDL_COMMON_STARTMENU); setShellFolderVariable(L"commonstartup", CSIDL_COMMON_STARTUP); setShellFolderVariable(L"cookies", CSIDL_COOKIES); setShellFolderVariable(L"desktop", CSIDL_DESKTOP); setShellFolderVariable(L"desktopdir", CSIDL_DESKTOPDIRECTORY); setShellFolderVariable(L"favorites", CSIDL_FAVORITES); setShellFolderVariable(L"fonts", CSIDL_FONTS); setShellFolderVariable(L"history", CSIDL_HISTORY); setShellFolderVariable(L"internet", CSIDL_INTERNET); setShellFolderVariable(L"internetcache", CSIDL_INTERNET_CACHE); setShellFolderVariable(L"nethood", CSIDL_NETHOOD); setShellFolderVariable(L"documentsdir", CSIDL_PERSONAL); setShellFolderVariable(L"printhood", CSIDL_PRINTHOOD); setShellFolderVariable(L"programs", CSIDL_PROGRAMS); setShellFolderVariable(L"recent", CSIDL_RECENT); setShellFolderVariable(L"sendto", CSIDL_SENDTO); setShellFolderVariable(L"startmenu", CSIDL_STARTMENU); setShellFolderVariable(L"startup", CSIDL_STARTUP); setShellFolderVariable(L"templates", CSIDL_TEMPLATES); setShellFolderVariable(L"commonadmintoolsdir", CSIDL_COMMON_ADMINTOOLS); setShellFolderVariable(L"admintoolsdir", CSIDL_ADMINTOOLS); // // Set version identification variables // struct VersionToVariable { UINT uVersion; LPCWSTR pszVariable; } versions[] = \ { { WINVER_WIN95, L"Win95" }, { WINVER_WIN98, L"Win98" }, { WINVER_WINME, L"WinME" }, { WINVER_WINNT4, L"WinNT4" }, { WINVER_WIN2000, L"Win2000" }, { WINVER_WINXP, L"WinXP" }, { WINVER_VISTA, L"WinVista" }, { WINVER_WIN7, L"Win7" }, { WINVER_WIN8, L"Win8" }, { WINVER_WIN81, L"Win81" }, { WINVER_WIN2003, L"Win2003" }, { WINVER_WHS, L"Win2003" }, // WHS is Win2003 in disguise { WINVER_WIN2008, L"Win2008" }, { WINVER_WIN2008R2, L"Win2008R2" }, { WINVER_WIN2012, L"Win2012" }, { WINVER_WIN2012R2, L"Win2012R2" } }; UINT uVersion = GetWindowsVersion(); for (size_t idx = 0; idx < COUNTOF(versions); ++idx) { if (versions[idx].uVersion == uVersion) { pSM->SetVariable(versions[idx].pszVariable, L"true"); } else { pSM->SetVariable(versions[idx].pszVariable, L"false"); } } if (IsOS(OS_NT)) { pSM->SetVariable(L"Win9x", L"false"); pSM->SetVariable(L"WinNT", L"true"); } else { pSM->SetVariable(L"Win9x", L"true"); pSM->SetVariable(L"WinNT", L"false"); } #if defined(_WIN64) pSM->SetVariable(L"Win64", L"true"); #else if (IsOS(OS_WOW6432)) { pSM->SetVariable(L"Win64", L"true"); } else { pSM->SetVariable(L"Win64", L"false"); } #endif // screen resolution StringCchPrintfW(wzTemp, MAX_PATH, L"%d", GetSystemMetrics(SM_CXSCREEN)); pSM->SetVariable(L"ResolutionX", wzTemp); StringCchPrintfW(wzTemp, MAX_PATH, L"%d", GetSystemMetrics(SM_CYSCREEN)); pSM->SetVariable(L"ResolutionY", wzTemp); // build date/time from PE headers getCompileTime(wzTemp, MAX_PATH); pSM->SetVariable(L"CompileDate", wzTemp); #if defined(LS_CUSTOM_INCLUDEFOLDER) pSM->SetVariable(L"IncludeFolder", L"1"); #endif // LS_CUSTOM_INCLUDEFOLDER }