Wednesday 28 August 2013

Re: Scopes and the app store in 14.04

Hi Jamie,

thanks for taking the time to write all this up!

Lengthy reply interspersed below…

> To increase scopes development velocity, the scopes team will be using ICE
> (zeroc-ice) for scopes. ICE normally uses TCP/UDP and opens a socket on the
> loopback interface. This is very problematic for application confinement because
> apps that are allowed network access (the default in the SDK) would be able to
> connect to any of the loopback listeners. There are a number of ways to solve
> this, but the most elegant is to implement a unix domain socket plugin for ICE.
> The scopes team have done this and with this change and all scopes adopting it,
> application confinement can prevent apps from attacking the system.

The domain socket plug-in for Ice is working fine. With that, access control could be implemented by placing the socket on which a scope listens for queries into a directory specific to that scope, and then setting search permission on or off for that directory based on UID/GID/others. (We have to use permissions on the directory because bind() creates a domain socket using umask permission. We cannot guarantee that this will be correct, even if we set umask ourselves, because that's full of race conditions. We would need a per-thread umask, which doesn't exist.)

The domain socket path is not mandatory. I can change the scopes machinery to use TCP instead by editing a few configuration files. No recompilation necessary. In that case, scopes would listen on localhost on a specific port for incoming requests (two ports actually per scope, for reasons having to do with threading). In this case, we would have to administer port numbers manually, so each scope uses fixed immutable port numbers, and then apparmor would have to somehow decide whether a process is allowed to connect to that port or not.

> As for confining scopes themselves, this is a hard problem because some scopes
> will need wide access to the filesystem, some networking to other sites, many to
> canonical.com/ubuntu.com. There are a few things that are useful with the
> current design:
> * only Canonical-trusted scopes will likely be able to share a process

Any scopes that "trust each other" can be collocated in the same process. For example, scopes from the same author could run in the same process. Whether or not scopes share a process does not affect the scopes implementation. How to group scopes into processes is decided via configuration files.

> * any scopes requiring authentication credentials needs to be in its
> own process (to prevent other scopes from accessing shared memory)

Yes. Any scope that "has secrets" needs to run in its own process. When installing a scope, we just put the scope into a group of its own, so it gets run as a separate process.

> * it is possible that we could make it a requirement that scopes from
> the appstore (ie, non-Canonical scopes) must be in their own process

I see no other option. Anything that's installed from a click package or something like that must end up running as a separate process.

> * we can trigger manual reviews for scopes in general, or scopes that
> need more flexible confinement. This is not optimal because we are striving
> for as little code-reviews as possible in the app store because it just
> doesn't scale.

I agree. Less of this is better.

> * we are able to revoke scopes from the app store if they are malicious

Right. But that's no different from applications, really. And, of course, once installed, a malicious scope or application can continue to do damage.

> * appstore ratings will help self-regulate
>
> A few things not in our favor:
> * scopes can by design do anything

Yes. There is nothing we can do about this. The scope is a shared library that we load at run time with dlopen(). I cannot control what actions are taken by the code I call in that library.

> * scopes can be aggregated in any number of ways
> * due to aggregation, scopes are supposed to be able to communicate
> with arbitrary other scopes
> * even local scopes may be required to access canonical.com/ubuntu.com
> servers for side channel communications for recommendations

At the very least, scopes will need to be able to reach the smart scopes server, issuing REST request over HTTP. This appears to be non-negotiable.

For scopes running on the local device, if we do not allow them to access the network, they will be limited to producing information that can be found in the file system only. But that has serious drawbacks. In particular, I could not do things such as make a scope that, say, shows me recent emails from my imap server or that connects to a news site behind a paywall and feeds me headlines.

The *only* way to run a scope that gets data that requires login credentials is to run the scope locally. (Remote scopes cannot do anything that requires authentication for a particular user.) But, pretty much by definition, if I run something locally and it needs a password, this means that the local thing is going remote with that password…

Ideally, it should be possible to, on a per-scope basis, configure permissions that specify where a process can and can't connect to. That way, we could limit a scope to only connect to data sources that are legit, rather than being able to roam the net at will and funnel away confidential information.

> Confining individual scopes requires a lot of thought and discussion. Some
> (non-exhaustive) discussion points:
> * can we agree that app store scopes must always run in their own process?

I consider that a given. We can't collocate scopes from different authors. Even disregarding the security implications, it makes sense not to collocate just as a protection against bugs. If one scope has a bug and segfaults, it'll take down all its collocated scopes with it…

> * can we agree to kill a scope and all of its children if the scope is disabled
> or if the privacy setting is toggled to 'on'

I cannot know what the children of a scope are. The scope can fork/exec and damonize itself, and there is nothing I can do to stop that. I cannot use process parent/child relationships to track this, for example, and I don't think there is any other reliable way.

If a scope is disabled, we can set the permissions on the directory for its incoming sockets to 000, so no-one can connect to that scope anymore. But that's about it. We'd have to make sure that the directory is owned by a special scope user ID, obviously.

> * can we agree that scopes must use the unix domain socket ICE plugin? How can
> we enforce this? (We should be able to prevent 'bind' for TCP and UDP using
> AppArmor IPC mediation, but perhaps the scopes architecture enforces this)

The scopes architecture can't enforce anything. If the scope makes a system call, I can't stop it.

> * do we imagine that most scopes won't need filesystem access beyond what app
> store apps typically would need? (essentially read access to the install dir
> and write access to app/scope specific directories following the XDG base dir
> specification). If so, we may be able to treat these scopes like ordinary
> click apps (using a different AppArmor policy template though) where the
> reviews are/will be automatic.

Some scopes deliver music and video content. Basically, they index media files in the local filesystem. This means monitoring mounts, checking the contents of removable media, looking in a number configurable locations for music files, etc. Generally denying file system access to such scopes will make them unusable.

> * A big problem is that some scopes are expected to have access to other parts
> of the filesystem. What is to stop a malicious scope from sending all the
> user's data off to the internet? Would it be possible to say that if a scope
> needed filesystem access like this, it is not allowed to connect to the
> network at all? (ie, either have networking/very limited filesystem access or
> no networking/less limited filesystem access). This seems quite tricky to get
> right

Seems like a good idea, buy I'm not totally sure of all the implications of this approach. There may well be a security hole in this policy that I'm not seeing at the moment.

> * can we clearly define how aggregation works and how app store scopes are
> intended to participate in aggregation? Would it help if we said that app
> store scope can only be an aggragatee and not an aggregator? I don't have a
> handle on aggregation yet to understand all the security implications....

A scope is simply a data provider that responds to queries that arrive via its network endpoint. The scope is not aware of the identity of the query source. I cannot stop a scope from aggregating from another source. What the scope does is up to the scope author. The only way to prevent scope A from aggregating from scope B is to make it impossible for A to connect to B's endpoint. As far as I can see, the only mechanism we currently have to enforce this are the file system permissions on the directory that contains the socket.

> = Issue #2: scopes privacy =
> Related to confining scopes is enforcing network access based on the privacy
> setting. The good news is that the new design for 14.04 doesn't require that
> scope authors write code to honor the setting. Instead, if privacy is toggled
> on, scopes simply aren't sent the request for data. Individual scopes can also
> be toggled on and off so when a scope is disabled, it isn't sent any data. This
> does not handle malicious scopes that access the network directly however-- ie,
> a scope that ignores SIGTERM/SIGSTOP and keeps running behind the scenes to
> shovel data over the network).

There is no difference between a privacy setting and the on/off setting, as far as I can see. Whether the scope process is running or not is really a secondary issue here. A scope that no-one can connect to is basically the same as a scope that isn't running at all.

As far as making a scope go away (in the sense of killing it), I believe this is impossible. We cannot know when the scope does a fork/exec, for example (unless apparmor can prevent that). If we indeed can use apparmor to prevent fork/exec, I need to know about it at least. Currently, I don't need fork/exec for what I'm doing. But I was thinking of possibly daemonizing the scopes processes. (Not sure yet whether that's really desirable.) If we daemonize, I will need fork (but not exec). In addition, the current daemonize_me() implementation uses /proc to close open file descriptors. That's way more efficient that trying to close open descriptors by calling close() on MAXFD file descriptors. If apparmor prevents access to /proc, we could change that part of the code to do the inefficient way. (Unfortunately, there is no reliable way in Unix to say "close all file descriptors > 2", or even "give me the largest open file descriptor".)

> To deal with both well-behaved and malicious scopes, there was the idea of
> toggling between mostly-permissive AppArmor profiles to turn networking on and
> off. Local scopes would always be off while smart scopes get toggled based on
> privacy setting. This would work for 13.10 but is greatly complicated going
> forward because networking is pretty much always needed for the side channel
> communications.

Smart scopes, by definition, have no privacy issues. Anything that runs on the smart scopes server can only deal in public information because there is no way to get per-user authentication data from the device to the SSS. Any credentials that a scope running on the SSS requires would have to be hard-baked into that scope.

> Perhaps the design is sufficient to address scopes privacy though-- disabled
> scopes aren't ever called/aggregated and the scopes architecture prevents
> sending scopes data when the privacy setting is off, so a malicious scope can't
> override being disabled or the user's privacy setting. We can add to that
> SIGKILL for it and its children when it is disabled or the privacy setting is
> toggled to on.

I can see the following options:

- No-one but Canonical (and trusted third-parties, such as OEMs) can install a scope on the device. That way, we can vet every line of code we execute. Problem solved, at the cost of making it impossible to install scopes from click packages.

- By design, scopes are meant to be able to do things that are security sensitive, such as logging into an imap server. (Such scopes *must* run locally.) If we want to prevent one scope from snooping on another, it seems we are restricted to what we can do with file system permissions and apparmor.

Following up on the second point:

- If a local scope has network access, that essentially means we must deny that scope file system access (except maybe for a carefully selected subtree).

- If a local scope has file system access, we must deny that scope network access, otherwise, it can funnel away data (unless we write the scope ourselves of course.)

- For aggregating scopes, we have to prevent privilege escalation. I am not sure whether this is achievable by using only file system permissions and apparmor. But, basically, if a scope that delivers sensitive information (either from a remote source or from the file system) can be spoken to by another scope that then can stash the data away in Russia, it's game over.

Another option we could explore is to run Ice over SSL. Ice has an SSL transport and, with appropriate certificates, we can control exactly who can connect to whom. What worries me about this scenario is the complexity. We'd have to generate certificates on the fly during installation, protect those certificates and keep track of them, generate the necessary configuration on the fly, etc. This is really complex and, even if we get it right technically, it could be very difficult to be sure that the end result is still secure. One little setting incorrect somewhere, and we have a hole…

The real problem here appears to be not so much the technical issues around controlling access to network endpoints and the like, but the design. Application confinement works because applications are isolated from each other. The idea of aggregation is anathema to that.

But, even if we remove the idea of aggregation altogether, we still have this problem. As long as there is IPC involved, we have network endpoints that are potentially vulnerable. In theory, a scope could cobble network packets together by hand and issue a request to an Ice endpoint without using any of the scopes or Ice runtime at all… (It's not difficult to do this.)

It seems the entire thing boils down to two points:

- If we use IPC, we need to be able to control who can talk to whom.

- Given that, we then need a policy that decides who can talk to whom, to stop privilege escalation and leaking of data.

If we assume that the first problem is solved, are we confident that we can come with a suitable policy? If not, we need to go back to the drawing board.

Michi.
--
ubuntu-devel mailing list
ubuntu-devel@lists.ubuntu.com
Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/ubuntu-devel