IPC security in mojo

430 views
Skip to first unread message

Joshua Pawlicki

unread,
Jun 16, 2022, 12:07:51 PM6/16/22
to chromium-mojo
In updater code, we have a requirement that certain RPCs can only be called from a client running at the same privilege level, whereas others are permitted to cross privilege levels.

For example, we might imagine there exists a singleton server/receiver process on the system, running as root, which exposes an API like:

ServerApi {
  GetVersion() => (string version)
  InstallApplication(string app_id) => (int result)
}

A caller running as waffles@ should be able to ask the server its version, but not call InstallApplication (only a client running as root should be able to call InstallApplication). Additionally, the server may wish to check the code signing status of the client before proceeding with the operation. (The same idea applies on Windows with crossing integrity levels.)

Basically, the problem here is one of trusted/powerful receivers and untrustworthy remotes; sort of the opposite of a more common (I think) pattern in Chromium of trusted/powerful callers dispatching tasks to untrustworthy helpers.

My understanding is that we'd have to implement such checks when we set up the message pipe. Does that sound right / is it sufficient? And does this mean that we shouldn't use the platform API (or at least not NamedPlatformChannel with guessable names) and invitations?

Or in general, do you have any other advice/guidance here?

K. Moon

unread,
Jun 16, 2022, 12:49:56 PM6/16/22
to Joshua Pawlicki, chromium-mojo
Just a minor observation, but this pattern is quite similar to how the (trusted) browser provides services to (untrusted) renderer processes, so I think these cases are more analogous than different.

One common pattern in Chromium is to pass/bind endpoints across the IPC boundary (something Mojo has good support for), so you don't need to expose all the services to anyone who asks; they need to request the binding from a broker first. (I may be confusing terminology here, as Mojo isn't something I spend a whole lot of time thinking about; it sorta Just Works for me.)

--
You received this message because you are subscribed to the Google Groups "chromium-mojo" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-moj...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-mojo/CAFE%3DDz0MtnbW0_CG41G8FZW8MtE7vBx%3DtncrbmNqWJy_avTKXg%40mail.gmail.com.

K. Moon

unread,
Jun 16, 2022, 12:51:34 PM6/16/22
to Joshua Pawlicki, chromium-mojo
Also, if you haven't seen this already, there's a pretty extensive guide on security best practices for Mojo:

Ken Rockot

unread,
Jun 16, 2022, 1:34:22 PM6/16/22
to Joshua Pawlicki, chromium-mojo
On Thu, Jun 16, 2022 at 12:07 PM Joshua Pawlicki <waf...@chromium.org> wrote:
In updater code, we have a requirement that certain RPCs can only be called from a client running at the same privilege level, whereas others are permitted to cross privilege levels.

For example, we might imagine there exists a singleton server/receiver process on the system, running as root, which exposes an API like:

ServerApi {
  GetVersion() => (string version)
  InstallApplication(string app_id) => (int result)
}

A caller running as waffles@ should be able to ask the server its version, but not call InstallApplication (only a client running as root should be able to call InstallApplication). Additionally, the server may wish to check the code signing status of the client before proceeding with the operation. (The same idea applies on Windows with crossing integrity levels.)

Basically, the problem here is one of trusted/powerful receivers and untrustworthy remotes; sort of the opposite of a more common (I think) pattern in Chromium of trusted/powerful callers dispatching tasks to untrustworthy helpers.

My understanding is that we'd have to implement such checks when we set up the message pipe. Does that sound right / is it

Yep.
 
sufficient? And does this mean that we shouldn't use the platform API (or at least not NamedPlatformChannel with guessable names) and invitations?

You must use invitations to interconnect processes. NamedPlatformChannel as-is probably won't get you what you want, but it could provide a starting point. In general you'll probably want to do something like:
  • connect your client to the service somehow establish a transport (e.g. connecting to a named socket)
  • authenticate the client immediately upon connection (e.g. on Linux the service might query the kernel for the socket client's uid)
  • send a Mojo invitation from the service over the transport, with a message pipe attached
  • use the authentication result to decide which interface to bind on the service side of the pipe
  • accept the invitation on the client side, assuming an appropriate interface on the attached pipe
So if you define:

interface Client {
  DoUnprivilegedStuff();
};

interface Superclient {
  DoPowerfulStuff();
  BindClient(pending_receiver<Client> r);
};

A root client would use the invitation pipe as a Remote<Superclient>, and other clients would use it as a Remote<Client>. The service would similarly bind the invitation pipe to either a Receiver<Superclient> or Receiver<Client>, as determined by your platform-specific authentication step.

Of course a badly behaved client can try to treat its pipe as a Remote<Superclient>, but its messages will still be routed to a Receiver<Client>.


Or in general, do you have any other advice/guidance here?

--

Joe Mason

unread,
Jun 17, 2022, 1:47:51 PM6/17/22
to K. Moon, Joshua Pawlicki, chromium-mojo
Just to highlight this link, it has a section exactly covering the case you're asking about.

Reply all
Reply to author
Forward
0 new messages