Commit a8140751 authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

chromeos: Adds two phase embedding to window server

For OOPIF we want the parent renderer to embed the child. This is
mediated by the browser. That is, we want something like the
following:

1. Browser obtains WindowTreeClient from child renderer (cWTC).
2. Browser passes cWTC to parent renderer.
3. Parent renderer creates Window in mus and calls Embed() with cWTC.

Unfortunately in step 2 if the parent renderer were compromised then
it could directly call functions on cWTC, such as spoofing
events. This is a security problem.

To avoid this scenario I'm adding the option of a two phase
embed. This results in the following:

1. Browser obtains WindowTreeClient from child renderer (cWTC).
2. Browser calls ScheduleEmbed() on it's WindowTree
   connection. ScheduleEmbed is a new function that takes a
   WindowTreeClient and returns a token.
3. Browser waits for token.
4. Browser passes token to parent renderer.
5. Parent renderer creates Window in mus and calls Embed() passing
   token.

With this flow renderers don't end up with a WindowTreeClient from a
different client.

BUG=758387
TEST=covered by tests

Change-Id: I819a57cd811d4939cbeecec8aeb8273eefca64f5
Reviewed-on: https://chromium-review.googlesource.com/699519Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#506476}
parent 28984916
......@@ -35,6 +35,7 @@ mojom("interfaces") {
"//gpu/ipc/common:interfaces",
"//media/gpu/mojo:jpeg_decoder",
"//media/mojo/interfaces",
"//mojo/common:common_custom_types",
"//services/ui/public/interfaces/cursor",
"//services/ui/public/interfaces/display",
"//services/ui/public/interfaces/ime",
......
......@@ -4,6 +4,7 @@
module ui.mojom;
import "mojo/common/unguessable_token.mojom";
import "services/ui/public/interfaces/cursor/cursor.mojom";
import "services/ui/public/interfaces/event_matcher.mojom";
import "services/ui/public/interfaces/mus_constants.mojom";
......@@ -247,6 +248,24 @@ interface WindowTree {
Embed(uint32 window_id, WindowTreeClient client, uint32 embed_flags)
=> (bool success);
// Schedules a future call to Embed() using the returned token. This is used
// when two clients need to work together to complete an embedding without
// passing the WindowTreeClient between the two. This ensures a client isn't
// able to spoof another client (say by directly passing events to the
// client).
//
// For example, client A embeds client B in a window. Client A wants client B
// to embed a WindowTreeClient in a window created by client B. This can be
// accomplished by client A calling ScheduleEmbed() and then passing the
// token returned from ScheduleEmbed() to client B (using a separate pipe) so
// that client B may call EmbedUsingToken().
ScheduleEmbed(WindowTreeClient client)
=> (mojo.common.mojom.UnguessableToken token);
EmbedUsingToken(uint32 window_id,
mojo.common.mojom.UnguessableToken token,
uint32 embed_flags)
=> (bool success);
// Sets focus to the specified window, use 0 to clear focus. For a window to
// get focus the following has to happen: the window is drawn, the window has
// been marked as focusable (see SetCanFocus()) and the window is in a
......
......@@ -1495,6 +1495,33 @@ ClientWindowId WindowTree::MakeClientWindowId(const WindowId& id) const {
return MakeClientWindowId((id.client_id << 16) | id.window_id);
}
mojom::WindowTreeClientPtr
WindowTree::GetAndRemoveScheduledEmbedWindowTreeClient(
const base::UnguessableToken& token) {
auto iter = scheduled_embeds_.find(token);
if (iter != scheduled_embeds_.end()) {
mojom::WindowTreeClientPtr client = std::move(iter->second);
scheduled_embeds_.erase(iter);
return client;
}
// There are no clients above the window manager.
if (window_manager_internal_)
return nullptr;
// Use the root to find the client that embedded this. For non-window manager
// connections there should be only one root (a WindowTreeClient can only be
// embedded once). During shutdown there may be no roots.
if (roots_.size() != 1)
return nullptr;
const ServerWindow* root = *roots_.begin();
WindowTree* owning_tree = window_server_->GetTreeWithId(root->id().client_id);
if (!owning_tree)
return nullptr;
DCHECK_NE(this, owning_tree);
return owning_tree->GetAndRemoveScheduledEmbedWindowTreeClient(token);
}
void WindowTree::NewWindow(
uint32_t change_id,
Id transport_window_id,
......@@ -1947,6 +1974,28 @@ void WindowTree::Embed(Id transport_window_id,
Embed(MakeClientWindowId(transport_window_id), std::move(client), flags));
}
void WindowTree::ScheduleEmbed(mojom::WindowTreeClientPtr client,
const ScheduleEmbedCallback& callback) {
const base::UnguessableToken token = base::UnguessableToken::Create();
scheduled_embeds_[token] = std::move(client);
callback.Run(token);
}
void WindowTree::EmbedUsingToken(Id transport_window_id,
const base::UnguessableToken& token,
uint32_t flags,
const EmbedUsingTokenCallback& callback) {
mojom::WindowTreeClientPtr client =
GetAndRemoveScheduledEmbedWindowTreeClient(token);
if (!client) {
DVLOG(1) << "EmbedUsingToken failed, no ScheduleEmbed(), token="
<< token.ToString();
callback.Run(false);
return;
}
Embed(transport_window_id, std::move(client), flags, callback);
}
void WindowTree::SetFocus(uint32_t change_id, Id transport_window_id) {
client()->OnChangeCompleted(
change_id, SetFocus(MakeClientWindowId(transport_window_id)));
......
......@@ -15,6 +15,7 @@
#include <vector>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
......@@ -435,6 +436,14 @@ class WindowTree : public mojom::WindowTree,
ClientWindowId MakeClientWindowId(Id transport_window_id) const;
ClientWindowId MakeClientWindowId(const WindowId& id) const;
// Returns the WindowTreeClient previously scheduled for an embed with the
// given |token| from ScheduleEmbed(). If this client is the result of an
// Embed() and ScheduleEmbed() was not called on this client, then this
// recurses to the parent WindowTree. Recursing enables an acestor to call
// ScheduleEmbed() and the ancestor to communicate the token with the client.
mojom::WindowTreeClientPtr GetAndRemoveScheduledEmbedWindowTreeClient(
const base::UnguessableToken& token);
// WindowTree:
void NewWindow(uint32_t change_id,
Id transport_window_id,
......@@ -497,6 +506,12 @@ class WindowTree : public mojom::WindowTree,
mojom::WindowTreeClientPtr client,
uint32_t flags,
const EmbedCallback& callback) override;
void ScheduleEmbed(mojom::WindowTreeClientPtr client,
const ScheduleEmbedCallback& callback) override;
void EmbedUsingToken(Id transport_window_id,
const base::UnguessableToken& token,
uint32_t flags,
const EmbedUsingTokenCallback& callback) override;
void SetFocus(uint32_t change_id, Id transport_window_id) override;
void SetCanFocus(Id transport_window_id, bool can_focus) override;
void SetEventTargetingPolicy(Id transport_window_id,
......@@ -707,6 +722,12 @@ class WindowTree : public mojom::WindowTree,
struct DragMoveState;
std::unique_ptr<DragMoveState> drag_move_state_;
// Holds WindowTreeClients passed to ScheduleEmbed(). Entries are removed
// when EmbedUsingToken() is called.
using ScheduledEmbeds =
base::flat_map<base::UnguessableToken, mojom::WindowTreeClientPtr>;
ScheduledEmbeds scheduled_embeds_;
// A weak ptr factory for callbacks from the window manager for when we send
// a image move. All weak ptrs are invalidated when a drag is completed.
base::WeakPtrFactory<WindowTree> drag_weak_factory_;
......
......@@ -59,6 +59,13 @@ void EmbedCallbackImpl(base::RunLoop* run_loop,
run_loop->Quit();
}
void ScheduleEmbedCallbackImpl(base::RunLoop* run_loop,
base::UnguessableToken* resulting_token,
const base::UnguessableToken& token) {
*resulting_token = token;
run_loop->Quit();
}
// -----------------------------------------------------------------------------
bool EmbedUrl(service_manager::Connector* connector,
......@@ -90,6 +97,27 @@ bool Embed(WindowTree* tree, Id root_id, mojom::WindowTreeClientPtr client) {
return result;
}
bool EmbedUsingToken(WindowTree* tree,
Id root_id,
const base::UnguessableToken& token) {
bool result = false;
base::RunLoop run_loop;
const uint32_t embed_flags = 0;
tree->EmbedUsingToken(root_id, token, embed_flags,
base::Bind(&EmbedCallbackImpl, &run_loop, &result));
run_loop.Run();
return result;
}
void ScheduleEmbed(WindowTree* tree,
mojom::WindowTreeClientPtr client,
base::UnguessableToken* token) {
base::RunLoop run_loop;
tree->ScheduleEmbed(std::move(client),
base::Bind(&ScheduleEmbedCallbackImpl, &run_loop, token));
run_loop.Run();
}
void GetWindowTree(WindowTree* tree,
Id window_id,
std::vector<TestWindow>* windows) {
......@@ -2040,6 +2068,72 @@ TEST_F(WindowTreeClientTest, EmbedSupplyingWindowTreeClient) {
SingleChangeToDescription(*client2.tracker()->changes()));
}
TEST_F(WindowTreeClientTest, EmbedUsingToken) {
// Embed client2.
ASSERT_TRUE(wt_client1()->NewWindow(1));
TestWindowTreeClient client2;
mojom::WindowTreeClientPtr client2_ptr;
mojo::Binding<WindowTreeClient> client2_binding(
&client2, mojo::MakeRequest(&client2_ptr));
ASSERT_TRUE(
Embed(wt1(), BuildWindowId(client_id_1(), 1), std::move(client2_ptr)));
client2.WaitForOnEmbed();
EXPECT_EQ("OnEmbed",
SingleChangeToDescription(*client2.tracker()->changes()));
// Schedule an embed of |client3| from wt1().
TestWindowTreeClient client3;
mojom::WindowTreeClientPtr client3_ptr;
mojo::Binding<WindowTreeClient> client3_binding(
&client3, mojo::MakeRequest(&client3_ptr));
base::UnguessableToken token;
ScheduleEmbed(wt1(), std::move(client3_ptr), &token);
// Have |client2| embed using the token scheduled above.
const Id window_id = client2.NewWindow(121);
ASSERT_TRUE(window_id);
ASSERT_TRUE(EmbedUsingToken(client2.tree(), BuildWindowId(client_id_2(), 121),
token));
client3.WaitForOnEmbed();
EXPECT_EQ("OnEmbed",
SingleChangeToDescription(*client3.tracker()->changes()));
// EmbedUsingToken() should fail when passed a token that was already used.
EXPECT_FALSE(EmbedUsingToken(client2.tree(),
BuildWindowId(client_id_2(), 121), token));
// EmbedUsingToken() should fail when passed a locally generated token.
EXPECT_FALSE(EmbedUsingToken(client2.tree(),
BuildWindowId(client_id_2(), 121),
base::UnguessableToken::Create()));
}
TEST_F(WindowTreeClientTest, EmbedUsingTokenFailsWithInvalidWindow) {
// Embed client2.
ASSERT_TRUE(wt_client1()->NewWindow(1));
TestWindowTreeClient client2;
mojom::WindowTreeClientPtr client2_ptr;
mojo::Binding<WindowTreeClient> client2_binding(
&client2, mojo::MakeRequest(&client2_ptr));
ASSERT_TRUE(
Embed(wt1(), BuildWindowId(client_id_1(), 1), std::move(client2_ptr)));
client2.WaitForOnEmbed();
EXPECT_EQ("OnEmbed",
SingleChangeToDescription(*client2.tracker()->changes()));
// Schedule an embed of |client3| from wt1().
TestWindowTreeClient client3;
mojom::WindowTreeClientPtr client3_ptr;
mojo::Binding<WindowTreeClient> client3_binding(
&client3, mojo::MakeRequest(&client3_ptr));
base::UnguessableToken token;
ScheduleEmbed(wt1(), std::move(client3_ptr), &token);
// This should fail as the window id does not identify a valid window.
EXPECT_FALSE(EmbedUsingToken(client2.tree(),
BuildWindowId(client_id_2(), 121), token));
}
TEST_F(WindowTreeClientTest, EmbedFailsFromOtherClient) {
ASSERT_NO_FATAL_FAILURE(EstablishSecondClient(true));
......
......@@ -94,6 +94,12 @@ void WindowPortMus::Embed(
window_tree_client_->Embed(window_, std::move(client), flags, callback);
}
void WindowPortMus::ScheduleEmbed(
ui::mojom::WindowTreeClientPtr client,
base::OnceCallback<void(const base::UnguessableToken&)> callback) {
window_tree_client_->ScheduleEmbed(std::move(client), std::move(callback));
}
std::unique_ptr<viz::ClientLayerTreeFrameSink>
WindowPortMus::RequestLayerTreeFrameSink(
scoped_refptr<viz::ContextProvider> context_provider,
......
......@@ -89,6 +89,12 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus {
uint32_t flags,
const ui::mojom::WindowTree::EmbedCallback& callback);
// Schedules an embed of a client. See
// mojom::WindowTreeClient::ScheduleEmbed() for details.
void ScheduleEmbed(
ui::mojom::WindowTreeClientPtr client,
base::OnceCallback<void(const base::UnguessableToken&)> callback);
std::unique_ptr<viz::ClientLayerTreeFrameSink> RequestLayerTreeFrameSink(
scoped_refptr<viz::ContextProvider> context_provider,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
......
......@@ -12,6 +12,8 @@
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
......@@ -345,6 +347,13 @@ void WindowTreeClient::Embed(
callback);
}
void WindowTreeClient::ScheduleEmbed(
ui::mojom::WindowTreeClientPtr client,
base::OnceCallback<void(const base::UnguessableToken&)> callback) {
tree_->ScheduleEmbed(std::move(client),
base::AdaptCallbackForRepeating(std::move(callback)));
}
void WindowTreeClient::AttachCompositorFrameSink(
Id window_id,
viz::mojom::CompositorFrameSinkRequest compositor_frame_sink,
......
......@@ -153,6 +153,12 @@ class AURA_EXPORT WindowTreeClient
uint32_t flags,
const ui::mojom::WindowTree::EmbedCallback& callback);
// Schedules an embed of a client. See
// mojom::WindowTreeClient::ScheduleEmbed() for details.
void ScheduleEmbed(
ui::mojom::WindowTreeClientPtr client,
base::OnceCallback<void(const base::UnguessableToken&)> callback);
void AttachCompositorFrameSink(
Id window_id,
viz::mojom::CompositorFrameSinkRequest compositor_frame_sink,
......
......@@ -255,6 +255,14 @@ void TestWindowTree::Embed(uint32_t window_id,
uint32_t flags,
const EmbedCallback& callback) {}
void TestWindowTree::ScheduleEmbed(ui::mojom::WindowTreeClientPtr client,
const ScheduleEmbedCallback& callback) {}
void TestWindowTree::EmbedUsingToken(uint32_t window_id,
const base::UnguessableToken& token,
uint32_t embed_flags,
const EmbedUsingTokenCallback& callback) {}
void TestWindowTree::SetFocus(uint32_t change_id, uint32_t window_id) {
OnChangeReceived(change_id, WindowTreeChangeType::FOCUS);
}
......
......@@ -182,6 +182,12 @@ class TestWindowTree : public ui::mojom::WindowTree {
ui::mojom::WindowTreeClientPtr client,
uint32_t flags,
const EmbedCallback& callback) override;
void ScheduleEmbed(ui::mojom::WindowTreeClientPtr client,
const ScheduleEmbedCallback& callback) override;
void EmbedUsingToken(uint32_t window_id,
const base::UnguessableToken& token,
uint32_t embed_flags,
const EmbedUsingTokenCallback& callback) override;
void SetFocus(uint32_t change_id, uint32_t window_id) override;
void SetCanFocus(uint32_t window_id, bool can_focus) override;
void SetEventTargetingPolicy(uint32_t window_id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment