blob: 0ec642ab8dcdf4e8fa2a05d84d579470c54bccb3 [file] [view]
<!-- mdformat off(templates not supported) -->
{% set rfcid = "RFC-0150" %}
{% include "docs/contribute/governance/rfcs/_common/_rfc_header.md" %}
# {{ rfc.name }}: {{ rfc.title }}
<!-- SET the `rfcid` VAR ABOVE. DO NOT EDIT ANYTHING ELSE ABOVE THIS LINE. -->
<!-- mdformat on -->
## Summary
We describe a new ability for product owners to allow users of their products to
opt out of receiving software updates. We describe the policies associated with
this mechanism, the ways in which it should be used, and the security mechanisms
associated with storing the setting. Finally, we describe how other components
on the system may observe whether the system is opted out of updates.
## Motivation
We have a requirement from customers to allow users of Fuchsia devices to opt
out of receiving updates. We must satisfy that requirement in a way that is as
secure as possible for all users, regardless of their opt-out status.
## Stakeholders
_Facilitator:_
pascallouis@google.com
_Reviewers:_
* kevinwells@google.com - Software Delivery (SWD)
* ampearce@google.com - Security
* pascallouis@google.com - Fuchsia Engineering Council (FEC)
_Consulted:_
* borthakur@google.com
* marvinpaul@google.com - Server implementation
* gtsai@google.com - Server implementation
* enoharemaien@google.com - Privacy
* hjfreyer@google.com - Component Platform / FEC
* Software Delivery team
_Socialization:_
This RFC went through a design phase with the Software Delivery team, the
Security and Privacy teams, and customers.
## Requirements
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in
[IETF RFC 2119](https://tools.ietf.org/html/rfc2119).
* Product owners may allow users to opt out of updates, which means no updates
are downloaded or installed to a user's device, except for critical
updates designated by the product owner, including critical security updates.
* If a device is opted out of updates and has a Factory Data Reset (FDR) run, it
should be opted back into updates.
* The setting must persist across reboots without having to be re-set on every
boot by a product-level component (which would cause race conditions between
the product-level component and update checks).
* The enablement and storage of the setting must be as secure as possible, to
prevent an attacker finding a privilege escalation, opting the device out of
updates, and persisting that vulnerability indefinitely.
* It must not be possible for someone to enable the opt-out option at runtime if
the product owner has not decided to include it in a build (there must be a
static compile-time flag to disable the feature entirely).
* We must have metrics on how many users (though not which specific users) are
opted out of updates, to ensure that this option is not being exploited by
attackers to persist vulnerabilities.
* If the system is in recovery mode, updates should always be allowed.
* If a user manually requests an update, even while updates are disabled, that
update should be allowed.
* This feature should only be enabled on devices with tamper-resistant storage,
such as Replay Protected Memory Blocks (RPMB). We have no requirements to
enable this feature on devices without tamper-resistant storage, but we can
revisit this RFC if the need arises in future.
* _Note_: The proposed design does not limit us only to secure storage
implementations, but removing tamper-resistant storage as a requirement
would lead to security tradeoffs for the product owner.
## Design
`omaha-client` is our production system update checker. It communicates with an
Omaha server run by the product owner or a delegate. Together, the Omaha client
and server periodically negotiate a system or package version to install.
We propose that a product owner can enable the existence of this feature on a
product by statically building in a flag in the Software Delivery configuration.
### Reading and writing the opt-out option value
A new SWD component called `update-settings-storage` will serve a FIDL API
called `fuchsia.update.config.OptOut` to read this option and an API called
`fuchsia.update.config.OptOutAdmin` to write this option. This API will need to
be exposed in the SDK to allow product-level components to toggle the option's
value on and off.
The `fuchsia.update.config.OptOutAdmin` API must be strictly protected by
capability routing and Scrutiny verification, to ensure that no unauthorized
components gain access to it.
_Reading_ the value of the opt-out setting using
`fuchsia.update.config.OptOut` should be allowed to allowlisted components on
the system with looser controls than `OptOutAdmin`. This will allow components
at the system and product level to make decisions based on whether the device is
opted out of updates, and provide settings and troubleshooting views of this
option.
### Opt-out persistence and security
The component serving the `OptOut` API, `update-settings-storage`, must persist
the value of the opt-out setting across reboots, and must persist the setting in
integrity-protected storage. For instance, `minfs` storage of this
setting without an accompanying hash and signature on the storage would be
insecure, and is disallowed.
We propose using hardware tamper-resistant storage (currently the only option on
Fuchsia devices is RPMB) to store this opt-out setting. The property we desire
from tamper-resistant storage is that it cannot be written to except by a signed
Trusted Application, or rogue writes can be detected.
These storage APIs exist on the required [products][glossary.product] and should
be exposed through the Verified Execution Trusted Application (VX TA) which is
signed and authenticated via hardware measures at boot.
### Update Checks
On each scheduled update check `omaha-client` should read from
`update-settings-storage` using the `OptOut` FIDL API, and send the opt-out
value to Omaha using the existing [`updatedisabled`][omaha-updatedisabled] field
in the Omaha protocol.
If the system is running on the recovery partition, `updatedisabled` should
always be `false`. Similarly, if the update check was user-initiated,
`updatedisabled` should always be `false`.
If the Omaha server receives an update check with `updatedisabled` equal to
`true`, it should return `NoUpdate` for that Omaha Application's response,
except for critical updates designated by the product owner.
There is an alternative here, which is to not send any Omaha update checks at
all if the device is opted out of updates. However, that alternative denies the
product owner metrics on how many users are opted out of updates, and denies the
product owner the ability to push critical updates (by overriding
`updatedisabled` field if required, on the server).
The opt-out must apply to all applications for which `omaha-client` is checking
for updates, including system updates and single packages.
## Implementation
Implementation will take place between the SWD, firmware, security, and update
server teams. The task breakdown is roughly as follows, and should correspond to
CL chains to be submitted.
### Software Delivery
* Create `fuchsia.update.config.OptOut` FIDL API
* Implement `update-settings-storage` component
* Read `fuchsia.update.config.OptOut` from `omaha-client` at update check time,
forward to Omaha server as `updatedisabled` flag (unless the device is in
recovery or the check was user-initiated)
* Expose `fuchsia.update.config` in the SDK, and allowlist both protocols.
* Add a Cobalt metric to `omaha-client` to count the number of opted-out devices
* Expose the opt-out setting in Inspect data for `omaha-client`
### Firmware / Security
* Expose an API to RPMB via the Verified Execution Trusted Application (VX TA),
statically include that API only on supported devices
* Modify the VX TA to clear the opt-out flag during Factory Data Reset (which
it already mediates)
### Update Server
* Modify generation of Omaha rules to return `NoUpdate` for all Application
Update Check Requests if `updatedisabled` is true for that Application.
## Performance
There should be no noticeable impact on system or update check performance.
Update checks are infrequent (on the order of hours), and not particularly
latency sensitive.
## Ergonomics
Our read API for this setting will help reduce possible developer or user
confusion, as will Inspect and Cobalt logging of the opt-out state.
## Backwards Compatibility
This RFC presents no backwards compatibility issues that we know of.
## Security considerations
Updates are Fuchsia's most important security feature. Without updates, the
Fuchsia team cannot patch vulnerabilities in the platform. More importantly,
having this code exist on the device at all is a risk to **all users** whether
or not they've opted out of updates: if an attacker gains sufficient privileges,
they can change the update opt-out setting and increase their chances of
persisting forever.
This design attempts to mitigate risk in the following ways:
* Storing the setting in tamper-resistant memory (RPMB)
* Only allowing a specific Verified Execution Trusted Application (TA) to
modify the setting
* TAs are only accessible when a device is in locked mode with an
authenticated user logged in (after OOBE)
* The VX TA is much smaller and simpler than, e.g., minfs, and is almost
exclusively modified by the security and firmware teams
* Auditing and allowlisting the routing of the TA
* Retaining the ability to push critical updates (as defined by the product
owner) to devices
* The criteria defining absolutely necessary updates are out of scope of
this RFC
* We will continually know how many devices are opted out and can create
alerts if that number becomes suspicious
The following risks still exist:
* We have no control over the product-level code which will call the opt-out
API, and we must ensure that only highly-privileged components can access
it
* An attacker who has gained sufficient kernel privileges could still take
control of `update-settings-storage` or the VX TA and ask them to modify the
setting
The following security improvements are not scoped at this time, but could be
considered for future iterations:
* Adding a physical presence requirement (user must interact with the device
in some way, not just through a product-level component)
* Making the setting writeable from the bootloader only. This defends the
setting against a kernel compromise at the expense of UX and cross-platform
consistency
## Privacy considerations
This design does not significantly impact user privacy, as all logging or
propagation of opt-out status will go through a privacy-protected logging
service: either our Crash database via Inspect, or Cobalt.
## Testing
We'll integration test the `update-settings-storage` component, its interaction
with `omaha-client`, and the final requests to an Omaha server that
`omaha-client` produces in various states of opt-out.
We'll integration test the RPMB API via the Verified Execution Trusted
Application.
We'll also unit test the implementations in each of the components individually.
Finally, we'll engage with the testing teams to ensure that products which
enable this feature have end to end testing of their entire integrations,
similar to other update features.
## Documentation
We'll need to add documentation to the new FIDL API, as well as to our [OTA flow
documentation][ota-flow].
## Drawbacks, alternatives, and unknowns
### 'Tombstone' builds
We could implement an update-opt out by pushing a 'tombstone' build to devices
which indicates that it will no longer update.
This has a notable upside:
* The opt-out setting would be stored in Verified Boot Metadata, which is
immutable. This implies better security properties.
This also has some notable downsides:
* We'd need to generate a specific 'tombstone' build for every normal build we
push, for every product which enables this feature.
* The actual application of the opt-out would require an OTA update, which
violates our requirements.
### Store the opt-out value in mutable storage
We could avoid some complexity by storing the opt-out value in minfs, which
would allow this feature to be deployed to a greater range of products. However,
this makes it trivial for an attacker with privilege escalation to persist their
attacks indefinitely if they can gain write access to minfs. Gaining write
access to minfs is likely substantially easier than modifying the Verified
Execution Trusted Application's state, since the VX TA is much smaller than
minfs in terms of surface area, and easier to audit.
### If a user is opted out, omit update checks entirely
If a user is opted out, we could modify `omaha-client` to not check for updates
at all. This has a couple of drawbacks: we can't get Omaha-based metrics on how
many users are opted out, and a [product owner][glossary.product-owner] with
sufficient authorization can't ask devices to take critical updates.
## Prior art and references
The [Omaha protocol includes][omaha-updatedisabled] the `updatedisabled` flag
for essentially this reason: a client device telling the server that it's
checking for an update, but that download and installation should not be
performed.
The Chrome browser [supports disabling updates][chrome-disable-updates] via
enterprise policy. This may be a use case for potential Fuchsia devices, but we
have no enterprise policy requirements at the moment.
[chrome-disable-updates]: https://support.google.com/chrome/a/answer/9838774
[glossary.product]: /docs/glossary/README.md#product
[glossary.product-owner]: /docs/glossary/README.md#product-owner
[omaha-updatedisabled]: https://github.com/google/omaha/blob/ebc25b2b3d77eed3d9a122bcfd89a66f6f192e4b/doc/ServerProtocolV3.md#updatecheck-request
[ota-flow]: /docs/concepts/packages/ota.md