Windows Service Silent Update
Overview
Before this task, when a user installs into the default Program Files directory, updates cannot be applied without us first prompting the user for elevated permissions with a User Account Control (UAC) dialog.
After this task, Firefox will use a service to execute updates so that UAC prompts are not displayed. The service is on demand and will remain stopped until it is needed, when it is needed it will be started again for the period of the update. If there are any problems updating through the service, it will fall back to updating the old way via the UAC prompt.
The task related to creating a service and not prompting the user for UAC is in bug 481815.
The feature page is located at: this feature page.
The security related bugs needed for 481815 to land were: bug 708688, bug 708697, bug 708690, bug 708778, bug 708854, bug 699700, bug 704285, bug 709173, bug 709183, and bug 709158.
Windows Service component information
Display name: Mozilla Maintenance Service
Service name: MozillaMaintenance
File name: maintenanceservice.exe
Installation directory: %PROGRAMFILES%\Mozilla Maintenance Service
Other details about the service:
- The service will be dealing with user tokens and therefore must be run as the SYSTEM account.
- The service will be located in the tree under /toolkit/components/maintenanceservice
To test and see if the service is installed, from a command prompt you can run:
sc query MozillaMaintenance
If it is installed you will see output of:
SERVICE_NAME: MozillaMaintenance TYPE : 10 WIN32_OWN_PROCESS STATE : 1 STOPPED WIN32_EXIT_CODE : 1077 (0x435) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
If it is not installed, you will see output of:
[SC] EnumQueryServicesStatus:OpenService FAILED 1060: The specified service does not exist as an installed service.
How the service works
Since the service is only started when needed, we pass the information to the service via command line on service start. The service security will be modified with a special ACE which allows non elevated processes to start and stop it. Setting these permissions on the service is needed because by default services can only be started and stopped by elevated processes. This is possible via the Win32 API SetServiceObjectSecurity.
The service will log all operations of the last update, as well as backup the 5 update operation logs before it. The install process and update processes will also be logged separately. The logs will live in the all users application data.
Since the service executes an update via updater.exe in session 0, no UI will be displayed. A UI could be displayed by running updater.exe with a user token instead, but to avoid permission problems on files, and inconsistencies in different OS versions and user types, the update will always be run as session 0.
A single service and service versioning
The service is currently only used on Firefox, but we do plan to allow other products to use the service. Only one service will exist across all channels, and once other products are allowed to use the service they will likely also share the service.
If a service is already installed, the service will be replaced on updates and installs only if it is newer than what is installed. This means that if a user has Nightly installed, the Nightly service will be used to update all channels.
If the user is running on an x64 computer with a native x64 build, then the service will not be installed nor used by that build. If the user is on an x64 computer with an x86 build, then the service will be used and installed by the build as an x86 process. Future versions of Firefox will support native x64 builds with the service. Work is being tracked for native x64 builds in Bug 715876.
The service decides whether or not it is newer by looking at the updater file's version number in comparison to a new one being updated/installed. Only if the newer version number is greater will it be replaced. This means that if the user runs builds like Nightly, then the Nightly service would update all other channels. It will therefore always be backwards compatible.
Service command line parameters
A service operation is executed by starting the service with special command line parameters.
The command line parameters are as follows:
- The name of the service: MozillaMaintenance
- The name of the service command to run. Currently only the "software-update" command is allowed.
- All service-command specific command line arguments.
Other commands being considered include clearing prefetch for faster startup, defraging user profile data, and other actions not yet discussed.
The "software-update" command specific command line arguments are as follows:
- MozillaMaintenance
- software-update
- updater.exe
- update-dir
- apply-dir
- [wait-pid]
- [callback-dir] (unused by the service but should be passed)
- [callback-path] (unused by the service but should be passed, updater.exe uses this to determine which application to lock to avoid re-launching errors)
- [callback args] (unused by the service but should be passed)
Note: The normal updater.exe command line parameters are as follows: updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
Applying an update from Firefox's perspective
- On Firefox (or any other Mozilla application that uses the service) startup, Firefox detects an update is ready to be applied
- Whether or not the service should be used, Firefox executes updater.exe unelevated.
- Firefox shuts itself down.
- updater.exe unelevated checks to see if the service should be used, and if so tries to start the service with the appropriate command line parameters.
- If the service can't be started, is disabled, or does not exist, Firefox will execute updater.exe as it used to without the service.
- If the service was used, updater.exe unelevated waits for the service to be stopped, which indicates the update is done.
- Once done updater.exe unelevated will run the callback application and the post update process.
Applying an update from the service's perspective
- The service gets started
- The service determines which action should be performed from its command line parameters.
- If the command is an action other than a software update, the action is performed. (None are supported yet)
- If the command is a software update then continue on...
- The service verifies the updater.exe file (See section Signing builds below)
- The service will execute the update with updater.exe under the context of its own session (session 0) using CreateProcess.
- The path of updater.exe will be a copy of the updater.exe which exists inside the service directory. (A copy in case the original gets overwritten during the update).
- The post update process (helper.exe) currently does i) system level stuff, and ii) user level stuff. This component will be executed twice. The system level stuff will also be executed under session 0 using CreateProcess. The user level stuff will be executed by the unelevated updater.exe with its own session ID.
- The service stops itself when it is done the operation.
When the service fails
- If there is an error creating the work item, we will fall back to the old way of updating: using updater.exe.
- If there is an error creating the process, or during update, or a sign check error, an error is written to update.status and info is reported via telemetry on the next restart of the callback application. The next restart of the callback application will also set the status back to pending so that the update will be applied next time without the service.
- If there are more than 10 consecutive service specific errors, the service will automatically disable itself and reset this count to 0. A user can re-enable the service via preferences.
Service as an optional component
- If the service is not installed, or disabled, we will fall back to the old way of updating: using updater.exe
- A user can chose to not install the service at install time of Firefox.
Limited user accounts
Whether a user is an administrator or a limited user account, they can initiate an update.
Service installation
The service will be installed for users automatically via software update. There are reasons why the service will not be installed though:
- An x64 native build is being used.
- An OS lower than XP is being used.
- The update is being performed as a limited user account.
- The service was previously installed and manually uninstalled. (registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\MaintenanceService and value Attempted=1 means it will never be attempted on update again).
If the service is installed, there will be a checkbox in the installer for whether or not to install the service component. The service will be able to be uninstalled separately; it will show up as a new item under add/remove programs.
Signing builds
To ensure we execute our own binary, we will verify the signature of updater.exe to make sure it is signed by us. Currently we do not sign Nightly builds (I don't think Aurora either), we would like to have this fixed so that the udpate process gets more testing before this hits mozilla-central.
Spoofing the wrong session ID
All commands will be executed in the context of session 0. Only the post update process and callback will be executed with the user session ID. These commands will not be executed by the service though so there is no chance of spoofing the session ID via work item file.
The session ID is not included in the work item files because a limited user account could launch a valid Firefox.exe into an admin session with the URL of a dangerous page.
Applying updates faster
Another bug which is not part of this task plans to move updates away from on startup, and to perform them to an alternate directory in the background.
Preferences
- There will be a new about:config option for whether or not to use the service. It will also be exposed in update preferences.
- The new setting will be a boolean setting called app.update.service.enabled.
- This will be defaulted to False if it does not exist, but we will set this to True for Firefox in new profiles. So for other products it will be defaulted to false, but for Firefox it will be defaulted to True.
- Other products who want to use the service in the future should mark the setting as True.
- There will be a new about:config option for keeping track of the number of service errors called app.update.service.errors.
- There will be a new about:config option for keeping track of the max number of service errors to occur before disabling the service via app.update.service.enabled.
- When app.update.service.errors reaches app.update.service.maxerrors, or 10 if maxerrors does not exist, the service will be disabled and app.update.service.errors will be reset to 0.
Test cases
Below I describe some important things that come to mind that we should be testing. All of the usual update tests and more should also be tested.
- Test that using a limited user account does not install the service, nor prompt to install the service.
- Test that if the service is already installed, installing an update of a higher service number will replace the old service.
- Test that if the service is already installed, installing an update of a lower service number will NOT replace the old service.
- Test that if the service is already installed, and another product with a higher version number gets installed, it will replace the old service.
- Test that if the service is already installed, and another product with a lower version number gets installed, it will NOT replace the old service.
- Test the same above rules cross architecture, from both x86 and x64.
- Test applying updates from a limited user account.
- Test applying updates from a Windows 2000 machine.
- Test applying updates from a Windows XP machine.
- Test applying updates from a Windows Vista machine at each of the UAC levels.
- Test applying updates from a Windows 7 machine at each of the UAC levels.
- Test doing 2 updates at once.
- Test having a service with the same name that is not ours, we should apply update the old way on the next browser startup after the one that should have updated.
- Test that limited user accounts have access to all of their files after an upgrade through the service.
QA's test plan can be found here: https://wiki.mozilla.org/Silent_Update_OS_Dialogs/TestPlan
Individual tests (Simona): http://bit.ly/v74fFt
Test slave requirements for automated testing (Ehsan): https://wiki.mozilla.org/Silent_Update_OS_Dialogs/Automated_testing/Test_slaves_requirements