Skip to content

Commit bf65684

Browse files
Alvin LeeChromium LUCI CQ
Alvin Lee
authored and
Chromium LUCI CQ
committed
dlp: Hook DLP ScopedFileAccess API into LocalFileStreamReader
As part of DLP file restrictions, the DLP daemon intercepts all file access and by default blocks all access if there isn't a matching admin rule that allows the op. There are two levels of DLP restrictions, there is a high level check within the Files.app which already applies file restrictions in the most part. The lower level DLP restrictions within the //storage/ layer actually requests an access token for the process to open the DLP restricted files. As a first step, we will grant access to all requests in the lower DLP layer by calling a DLP access request as a system component. This will be switched out for a regular DLP access request method additionally passing the destination target url when it can be obtained in //storage (see crbug.com/1354502). Change-Id: I53d9093280a8453e71faaa5b8a67627baf8308c4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3858118 Reviewed-by: Aya Elsayed <[email protected]> Reviewed-by: Austin Sullivan <[email protected]> Commit-Queue: Alvin Lee <[email protected]> Cr-Commit-Position: refs/heads/main@{#1045745}
1 parent 0e2f55b commit bf65684

7 files changed

+145
-11
lines changed

chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc

+12-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "base/process/process_handle.h"
88
#include "chromeos/dbus/dlp/dlp_client.h"
9+
#include "content/public/browser/browser_task_traits.h"
10+
#include "content/public/browser/browser_thread.h"
911

1012
namespace policy {
1113

@@ -68,9 +70,15 @@ void DlpScopedFileAccessDelegate::RequestFilesAccessForSystem(
6870
void DlpScopedFileAccessDelegate::PostRequestFileAccessToDaemon(
6971
const ::dlp::RequestFileAccessRequest request,
7072
base::OnceCallback<void(file_access::ScopedFileAccess)> callback) {
71-
client_->RequestFileAccess(
72-
request, base::BindOnce(&DlpScopedFileAccessDelegate::OnResponse,
73-
base::Unretained(this), std::move(callback)));
73+
// base::Unretained is safe as |client_| (global dbus singleton) outlives the
74+
// usage of |callback|.
75+
auto dbus_cb = base::BindOnce(
76+
&chromeos::DlpClient::RequestFileAccess, base::Unretained(client_),
77+
request,
78+
base::BindOnce(&DlpScopedFileAccessDelegate::OnResponse,
79+
base::Unretained(this), std::move(callback)));
80+
81+
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(dbus_cb));
7482
}
7583

7684
void DlpScopedFileAccessDelegate::OnResponse(
@@ -81,6 +89,7 @@ void DlpScopedFileAccessDelegate::OnResponse(
8189
std::move(callback).Run(file_access::ScopedFileAccess::Allowed());
8290
return;
8391
}
92+
8493
std::move(callback).Run(
8594
file_access::ScopedFileAccess(response.allowed(), std::move(fd)));
8695
}

chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class DlpScopedFileAccessDelegate
6161
const ::dlp::RequestFileAccessResponse response,
6262
base::ScopedFD fd);
6363

64+
// This is a pointer to a global dbus client singleton that is owned by the
65+
// browser instance. It is initialized and destructed at the same time as
66+
// dbus.
6467
raw_ptr<chromeos::DlpClient> client_;
6568
};
6669

storage/DEPS

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ include_rules = [
22
"+components/crash/core/common/crash_key.h",
33
"+components/services/filesystem",
44
"+components/services/storage",
5+
"+components/file_access",
56
"+crypto",
67
"+mojo",
78
"+net",

storage/browser/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ component("browser") {
239239
"//base/third_party/dynamic_annotations",
240240
"//build:chromeos_buildflags",
241241
"//components/crash/core/common:crash_key",
242+
"//components/file_access:file_access",
242243
"//components/services/storage/public/cpp",
243244
"//components/services/storage/public/cpp/filesystem",
244245
"//mojo/public/cpp/bindings",
@@ -345,6 +346,7 @@ source_set("unittests") {
345346
":test_support",
346347
"//base/test:test_support",
347348
"//build:chromeos_buildflags",
349+
"//components/file_access:file_access",
348350
"//components/services/filesystem/public/mojom",
349351
"//components/services/storage/public/cpp",
350352
"//mojo/public/cpp/system",

storage/browser/file_system/local_file_stream_reader.cc

+37-7
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
#include "base/check_op.h"
1414
#include "base/files/file_util.h"
1515
#include "base/location.h"
16+
#include "base/task/bind_post_task.h"
1617
#include "base/task/task_runner.h"
1718
#include "base/task/task_runner_util.h"
19+
#include "base/threading/sequenced_task_runner_handle.h"
1820
#include "base/types/pass_key.h"
21+
#include "components/file_access/scoped_file_access_delegate.h"
1922
#include "net/base/file_stream.h"
2023
#include "net/base/io_buffer.h"
2124
#include "net/base/net_errors.h"
@@ -93,16 +96,40 @@ void LocalFileStreamReader::Open(net::CompletionOnceCallback callback) {
9396
DCHECK(!stream_impl_.get());
9497
has_pending_open_ = true;
9598

96-
// Call GetLength first to make it perform last-modified-time verification,
97-
// and then call DidVerifyForOpen to do the rest.
98-
int64_t verify_result = GetLength(
99-
base::BindOnce(&LocalFileStreamReader::DidVerifyForOpen,
100-
weak_factory_.GetWeakPtr(), std::move(callback)));
99+
if (!file_access::ScopedFileAccessDelegate::Get()) {
100+
OnScopedFileAccessRequested(std::move(callback),
101+
file_access::ScopedFileAccess::Allowed());
102+
return;
103+
}
104+
105+
base::OnceCallback<void(file_access::ScopedFileAccess)> open_cb =
106+
base::BindOnce(&LocalFileStreamReader::OnScopedFileAccessRequested,
107+
weak_factory_.GetWeakPtr(), std::move(callback));
108+
auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
109+
auto task = base::BindPostTask(current_task_runner, std::move(open_cb));
110+
111+
// TODO(crbug.com/1354502): Replace this with actual destination URLs.
112+
file_access::ScopedFileAccessDelegate::Get()->RequestFilesAccessForSystem(
113+
{file_path_}, std::move(task));
114+
}
115+
116+
void LocalFileStreamReader::OnScopedFileAccessRequested(
117+
net::CompletionOnceCallback callback,
118+
file_access::ScopedFileAccess scoped_file_access) {
119+
if (!scoped_file_access.is_allowed()) {
120+
std::move(callback).Run(net::ERR_ACCESS_DENIED);
121+
return;
122+
}
123+
124+
int64_t verify_result = GetLength(base::BindOnce(
125+
&LocalFileStreamReader::DidVerifyForOpen, weak_factory_.GetWeakPtr(),
126+
std::move(callback), std::move(scoped_file_access)));
101127
DCHECK_EQ(verify_result, net::ERR_IO_PENDING);
102128
}
103129

104130
void LocalFileStreamReader::DidVerifyForOpen(
105131
net::CompletionOnceCallback callback,
132+
file_access::ScopedFileAccess scoped_file_access,
106133
int64_t get_length_result) {
107134
if (get_length_result < 0) {
108135
std::move(callback).Run(static_cast<int>(get_length_result));
@@ -114,12 +141,15 @@ void LocalFileStreamReader::DidVerifyForOpen(
114141
const int result = stream_impl_->Open(
115142
file_path_, kOpenFlagsForRead,
116143
base::BindOnce(&LocalFileStreamReader::DidOpenFileStream,
117-
weak_factory_.GetWeakPtr()));
144+
weak_factory_.GetWeakPtr(),
145+
std::move(scoped_file_access)));
118146
if (result != net::ERR_IO_PENDING)
119147
std::move(callback_).Run(result);
120148
}
121149

122-
void LocalFileStreamReader::DidOpenFileStream(int result) {
150+
void LocalFileStreamReader::DidOpenFileStream(
151+
file_access::ScopedFileAccess /*scoped_file_access*/,
152+
int result) {
123153
if (result != net::OK) {
124154
std::move(callback_).Run(result);
125155
return;

storage/browser/file_system/local_file_stream_reader.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "base/memory/weak_ptr.h"
1616
#include "base/time/time.h"
1717
#include "base/types/pass_key.h"
18+
#include "components/file_access/scoped_file_access.h"
1819
#include "net/base/completion_once_callback.h"
1920
#include "storage/browser/file_system/file_stream_reader.h"
2021

@@ -50,9 +51,14 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) LocalFileStreamReader
5051
void Open(net::CompletionOnceCallback callback);
5152

5253
// Callbacks that are chained from Open for Read.
54+
void OnScopedFileAccessRequested(
55+
net::CompletionOnceCallback callback,
56+
file_access::ScopedFileAccess scoped_file_access);
5357
void DidVerifyForOpen(net::CompletionOnceCallback callback,
58+
file_access::ScopedFileAccess scoped_file_access,
5459
int64_t get_length_result);
55-
void DidOpenFileStream(int result);
60+
void DidOpenFileStream(file_access::ScopedFileAccess /*scoped_file_access*/,
61+
int result);
5662
void DidSeekFileStream(int64_t seek_result);
5763
void DidOpenForRead(net::IOBuffer* buf,
5864
int buf_len,

storage/browser/file_system/local_file_stream_reader_unittest.cc

+83
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,50 @@
2222
#include "base/test/task_environment.h"
2323
#include "base/threading/thread.h"
2424
#include "base/time/time.h"
25+
#include "build/build_config.h"
26+
#include "components/file_access/scoped_file_access_delegate.h"
2527
#include "net/base/io_buffer.h"
2628
#include "net/base/net_errors.h"
2729
#include "net/base/test_completion_callback.h"
2830
#include "storage/browser/file_system/file_stream_reader_test.h"
2931
#include "storage/browser/file_system/file_stream_test_utils.h"
32+
#include "testing/gmock/include/gmock/gmock.h"
3033
#include "testing/gtest/include/gtest/gtest.h"
3134

35+
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
36+
#include "base/files/scoped_file.h"
37+
#endif
38+
3239
namespace storage {
3340

41+
namespace {
42+
43+
using ::testing::_;
44+
45+
class MockScopedFileAccessDelegate
46+
: public file_access::ScopedFileAccessDelegate {
47+
public:
48+
MOCK_METHOD3(
49+
RequestFilesAccess,
50+
void(const std::vector<base::FilePath>& files,
51+
const GURL& destination_url,
52+
base::OnceCallback<void(file_access::ScopedFileAccess)> callback));
53+
MOCK_METHOD2(
54+
RequestFilesAccessForSystem,
55+
void(const std::vector<base::FilePath>& files,
56+
base::OnceCallback<void(file_access::ScopedFileAccess)> callback));
57+
};
58+
59+
file_access::ScopedFileAccess CreateScopedFileAccess(bool allowed) {
60+
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
61+
return file_access::ScopedFileAccess(allowed, base::ScopedFD());
62+
#else
63+
return file_access::ScopedFileAccess(allowed);
64+
#endif
65+
}
66+
67+
} // namespace
68+
3469
class LocalFileStreamReaderTest : public FileStreamReaderTest {
3570
public:
3671
LocalFileStreamReaderTest() : file_thread_("TestFileThread") {}
@@ -100,4 +135,52 @@ INSTANTIATE_TYPED_TEST_SUITE_P(Local,
100135
FileStreamReaderTypedTest,
101136
LocalFileStreamReaderTest);
102137

138+
// TODO(crbug.com/1354502): Use RequestFileAccess() instead of
139+
// RequestFileAccessForSystem() when destionion URLs can be obtained in
140+
// //storage/.
141+
TEST_F(LocalFileStreamReaderTest, ReadAllowedByDataLeakPrevention) {
142+
this->WriteTestFile();
143+
std::unique_ptr<FileStreamReader> reader(
144+
this->CreateFileReader(std::string(this->kTestFileName), 0,
145+
this->test_file_modification_time()));
146+
147+
MockScopedFileAccessDelegate scoped_file_access_delegate;
148+
EXPECT_CALL(scoped_file_access_delegate, RequestFilesAccessForSystem(_, _))
149+
.WillOnce([&](const std::vector<base::FilePath>& files,
150+
base::OnceCallback<void(file_access::ScopedFileAccess)>
151+
callback) {
152+
std::move(callback).Run(CreateScopedFileAccess(true));
153+
});
154+
155+
int result = 0;
156+
std::string data;
157+
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
158+
ASSERT_EQ(net::OK, result);
159+
ASSERT_EQ(this->kTestData, data);
160+
}
161+
162+
// TODO(crbug.com/1354502): Use RequestFileAccess() instead of
163+
// RequestFileAccessForSystem() when destionion URLs can be obtained in
164+
// //storage/.
165+
TEST_F(LocalFileStreamReaderTest, ReadBlockedByDataLeakPrevention) {
166+
this->WriteTestFile();
167+
std::unique_ptr<FileStreamReader> reader(
168+
this->CreateFileReader(std::string(this->kTestFileName), 0,
169+
this->test_file_modification_time()));
170+
171+
MockScopedFileAccessDelegate scoped_file_access_delegate;
172+
EXPECT_CALL(scoped_file_access_delegate, RequestFilesAccessForSystem(_, _))
173+
.WillOnce([&](const std::vector<base::FilePath>& files,
174+
base::OnceCallback<void(file_access::ScopedFileAccess)>
175+
callback) {
176+
std::move(callback).Run(CreateScopedFileAccess(false));
177+
});
178+
179+
int result = 0;
180+
std::string data;
181+
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
182+
ASSERT_EQ(net::ERR_ACCESS_DENIED, result);
183+
ASSERT_EQ("", data);
184+
}
185+
103186
} // namespace storage

0 commit comments

Comments
 (0)