Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: chrome/browser/policy/cloud/resource_cache.cc

Issue 109743002: Move policy code into components/policy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: moar fixes Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/policy/cloud/resource_cache.h"
6
7 #include "base/base64.h"
8 #include "base/callback.h"
9 #include "base/file_util.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/logging.h"
12 #include "base/safe_numerics.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_util.h"
15
16 namespace policy {
17
18 namespace {
19
20 // Verifies that |value| is not empty and encodes it into base64url format,
21 // which is safe to use as a file name on all platforms.
22 bool Base64Encode(const std::string& value, std::string* encoded) {
23 DCHECK(!value.empty());
24 if (value.empty() || !base::Base64Encode(value, encoded))
25 return false;
26 base::ReplaceChars(*encoded, "+", "-", encoded);
27 base::ReplaceChars(*encoded, "/", "_", encoded);
28 return true;
29 }
30
31 // Decodes all elements of |input| from base64url format and stores the decoded
32 // elements in |output|.
33 bool Base64Encode(const std::set<std::string>& input,
34 std::set<std::string>* output) {
35 output->clear();
36 for (std::set<std::string>::const_iterator it = input.begin();
37 it != input.end(); ++it) {
38 std::string encoded;
39 if (!Base64Encode(*it, &encoded)) {
40 output->clear();
41 return false;
42 }
43 output->insert(encoded);
44 }
45 return true;
46 }
47
48 // Decodes |encoded| from base64url format and verifies that the result is not
49 // emtpy.
50 bool Base64Decode(const std::string& encoded, std::string* value) {
51 std::string buffer;
52 base::ReplaceChars(encoded, "-", "+", &buffer);
53 base::ReplaceChars(buffer, "_", "/", &buffer);
54 return base::Base64Decode(buffer, value) && !value->empty();
55 }
56
57 } // namespace
58
59 ResourceCache::ResourceCache(
60 const base::FilePath& cache_dir,
61 scoped_refptr<base::SequencedTaskRunner> task_runner)
62 : cache_dir_(cache_dir),
63 task_runner_(task_runner) {
64 }
65
66 ResourceCache::~ResourceCache() {
67 DCHECK(task_runner_->RunsTasksOnCurrentThread());
68 }
69
70 bool ResourceCache::Store(const std::string& key,
71 const std::string& subkey,
72 const std::string& data) {
73 DCHECK(task_runner_->RunsTasksOnCurrentThread());
74 base::FilePath subkey_path;
75 // Delete the file before writing to it. This ensures that the write does not
76 // follow a symlink planted at |subkey_path|, clobbering a file outside the
77 // cache directory. The mechanism is meant to foil file-system-level attacks
78 // where a symlink is planted in the cache directory before Chrome has
79 // started. An attacker controlling a process running concurrently with Chrome
80 // would be able to race against the protection by re-creating the symlink
81 // between these two calls. There is nothing in file_util that could be used
82 // to protect against such races, especially as the cache is cross-platform
83 // and therefore cannot use any POSIX-only tricks.
84 int size = base::checked_numeric_cast<int>(data.size());
85 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) &&
86 base::DeleteFile(subkey_path, false) &&
87 (file_util::WriteFile(subkey_path, data.data(), size) == size);
88 }
89
90 bool ResourceCache::Load(const std::string& key,
91 const std::string& subkey,
92 std::string* data) {
93 DCHECK(task_runner_->RunsTasksOnCurrentThread());
94 base::FilePath subkey_path;
95 // Only read from |subkey_path| if it is not a symlink.
96 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) ||
97 base::IsLink(subkey_path)) {
98 return false;
99 }
100 data->clear();
101 return base::ReadFileToString(subkey_path, data);
102 }
103
104 void ResourceCache::LoadAllSubkeys(
105 const std::string& key,
106 std::map<std::string, std::string>* contents) {
107 DCHECK(task_runner_->RunsTasksOnCurrentThread());
108 contents->clear();
109 base::FilePath key_path;
110 if (!VerifyKeyPath(key, false, &key_path))
111 return;
112
113 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES);
114 for (base::FilePath path = enumerator.Next(); !path.empty();
115 path = enumerator.Next()) {
116 const std::string encoded_subkey = path.BaseName().MaybeAsASCII();
117 std::string subkey;
118 std::string data;
119 // Only read from |subkey_path| if it is not a symlink and its name is
120 // a base64-encoded string.
121 if (!base::IsLink(path) &&
122 Base64Decode(encoded_subkey, &subkey) &&
123 base::ReadFileToString(path, &data)) {
124 (*contents)[subkey].swap(data);
125 }
126 }
127 }
128
129 void ResourceCache::Delete(const std::string& key, const std::string& subkey) {
130 DCHECK(task_runner_->RunsTasksOnCurrentThread());
131 base::FilePath subkey_path;
132 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path))
133 base::DeleteFile(subkey_path, false);
134 // Delete() does nothing if the directory given to it is not empty. Hence, the
135 // call below deletes the directory representing |key| if its last subkey was
136 // just removed and does nothing otherwise.
137 base::DeleteFile(subkey_path.DirName(), false);
138 }
139
140 void ResourceCache::Clear(const std::string& key) {
141 DCHECK(task_runner_->RunsTasksOnCurrentThread());
142 base::FilePath key_path;
143 if (VerifyKeyPath(key, false, &key_path))
144 base::DeleteFile(key_path, true);
145 }
146
147 void ResourceCache::FilterSubkeys(const std::string& key,
148 const SubkeyFilter& test) {
149 DCHECK(task_runner_->RunsTasksOnCurrentThread());
150
151 base::FilePath key_path;
152 if (!VerifyKeyPath(key, false, &key_path))
153 return;
154
155 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES);
156 for (base::FilePath subkey_path = enumerator.Next();
157 !subkey_path.empty(); subkey_path = enumerator.Next()) {
158 std::string subkey;
159 // Delete files with invalid names, and files whose subkey doesn't pass the
160 // filter.
161 if (!Base64Decode(subkey_path.BaseName().MaybeAsASCII(), &subkey) ||
162 test.Run(subkey)) {
163 base::DeleteFile(subkey_path, true);
164 }
165 }
166
167 // Delete() does nothing if the directory given to it is not empty. Hence, the
168 // call below deletes the directory representing |key| if all of its subkeys
169 // were just removed and does nothing otherwise.
170 base::DeleteFile(key_path, false);
171 }
172
173 void ResourceCache::PurgeOtherKeys(const std::set<std::string>& keys_to_keep) {
174 DCHECK(task_runner_->RunsTasksOnCurrentThread());
175 std::set<std::string> encoded_keys_to_keep;
176 if (!Base64Encode(keys_to_keep, &encoded_keys_to_keep))
177 return;
178
179 base::FileEnumerator enumerator(
180 cache_dir_, false, base::FileEnumerator::DIRECTORIES);
181 for (base::FilePath path = enumerator.Next(); !path.empty();
182 path = enumerator.Next()) {
183 const std::string name(path.BaseName().MaybeAsASCII());
184 if (encoded_keys_to_keep.find(name) == encoded_keys_to_keep.end())
185 base::DeleteFile(path, true);
186 }
187 }
188
189 void ResourceCache::PurgeOtherSubkeys(
190 const std::string& key,
191 const std::set<std::string>& subkeys_to_keep) {
192 DCHECK(task_runner_->RunsTasksOnCurrentThread());
193 base::FilePath key_path;
194 if (!VerifyKeyPath(key, false, &key_path))
195 return;
196
197 std::set<std::string> encoded_subkeys_to_keep;
198 if (!Base64Encode(subkeys_to_keep, &encoded_subkeys_to_keep))
199 return;
200
201 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES);
202 for (base::FilePath path = enumerator.Next(); !path.empty();
203 path = enumerator.Next()) {
204 const std::string name(path.BaseName().MaybeAsASCII());
205 if (encoded_subkeys_to_keep.find(name) == encoded_subkeys_to_keep.end())
206 base::DeleteFile(path, false);
207 }
208 // Delete() does nothing if the directory given to it is not empty. Hence, the
209 // call below deletes the directory representing |key| if all of its subkeys
210 // were just removed and does nothing otherwise.
211 base::DeleteFile(key_path, false);
212 }
213
214 bool ResourceCache::VerifyKeyPath(const std::string& key,
215 bool allow_create,
216 base::FilePath* path) {
217 std::string encoded;
218 if (!Base64Encode(key, &encoded))
219 return false;
220 *path = cache_dir_.AppendASCII(encoded);
221 return allow_create ? base::CreateDirectory(*path) :
222 base::DirectoryExists(*path);
223 }
224
225 bool ResourceCache::VerifyKeyPathAndGetSubkeyPath(const std::string& key,
226 bool allow_create_key,
227 const std::string& subkey,
228 base::FilePath* path) {
229 base::FilePath key_path;
230 std::string encoded;
231 if (!VerifyKeyPath(key, allow_create_key, &key_path) ||
232 !Base64Encode(subkey, &encoded)) {
233 return false;
234 }
235 *path = key_path.AppendASCII(encoded);
236 return true;
237 }
238
239
240 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/policy/cloud/resource_cache.h ('k') | chrome/browser/policy/cloud/resource_cache_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698