OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/policy/cloud/resource_cache.h" | 5 #include "chrome/browser/policy/cloud/resource_cache.h" |
6 | 6 |
7 #include "base/base64.h" | 7 #include "base/base64.h" |
8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
9 #include "base/files/file_enumerator.h" | 9 #include "base/files/file_enumerator.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/sequenced_task_runner.h" |
11 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
12 | 13 |
13 namespace policy { | 14 namespace policy { |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
17 // Verifies that |value| is not empty and encodes it into base64url format, | 18 // Verifies that |value| is not empty and encodes it into base64url format, |
18 // which is safe to use as a file name on all platforms. | 19 // which is safe to use as a file name on all platforms. |
19 bool Base64Encode(const std::string& value, std::string* encoded) { | 20 bool Base64Encode(const std::string& value, std::string* encoded) { |
20 DCHECK(!value.empty()); | 21 DCHECK(!value.empty()); |
(...skipping 25 matching lines...) Expand all Loading... |
46 // emtpy. | 47 // emtpy. |
47 bool Base64Decode(const std::string& encoded, std::string* value) { | 48 bool Base64Decode(const std::string& encoded, std::string* value) { |
48 std::string buffer; | 49 std::string buffer; |
49 ReplaceChars(encoded, "-", "+", &buffer); | 50 ReplaceChars(encoded, "-", "+", &buffer); |
50 ReplaceChars(buffer, "_", "/", &buffer); | 51 ReplaceChars(buffer, "_", "/", &buffer); |
51 return base::Base64Decode(buffer, value) && !value->empty(); | 52 return base::Base64Decode(buffer, value) && !value->empty(); |
52 } | 53 } |
53 | 54 |
54 } // namespace | 55 } // namespace |
55 | 56 |
56 ResourceCache::ResourceCache(const base::FilePath& cache_dir) | 57 ResourceCache::ResourceCache( |
57 : cache_dir_(cache_dir) { | 58 const base::FilePath& cache_dir, |
58 // Allow the cache to be created in a different thread than the thread that is | 59 scoped_refptr<base::SequencedTaskRunner> task_runner) |
59 // going to use it. | 60 : cache_dir_(cache_dir), |
60 DetachFromThread(); | 61 task_runner_(task_runner) { |
61 } | 62 } |
62 | 63 |
63 ResourceCache::~ResourceCache() { | 64 ResourceCache::~ResourceCache() { |
64 DCHECK(CalledOnValidThread()); | 65 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
65 } | 66 } |
66 | 67 |
67 bool ResourceCache::Store(const std::string& key, | 68 bool ResourceCache::Store(const std::string& key, |
68 const std::string& subkey, | 69 const std::string& subkey, |
69 const std::string& data) { | 70 const std::string& data) { |
70 DCHECK(CalledOnValidThread()); | 71 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
71 base::FilePath subkey_path; | 72 base::FilePath subkey_path; |
72 // Delete the file before writing to it. This ensures that the write does not | 73 // Delete the file before writing to it. This ensures that the write does not |
73 // follow a symlink planted at |subkey_path|, clobbering a file outside the | 74 // follow a symlink planted at |subkey_path|, clobbering a file outside the |
74 // cache directory. The mechanism is meant to foil file-system-level attacks | 75 // cache directory. The mechanism is meant to foil file-system-level attacks |
75 // where a symlink is planted in the cache directory before Chrome has | 76 // where a symlink is planted in the cache directory before Chrome has |
76 // started. An attacker controlling a process running concurrently with Chrome | 77 // started. An attacker controlling a process running concurrently with Chrome |
77 // would be able to race against the protection by re-creating the symlink | 78 // would be able to race against the protection by re-creating the symlink |
78 // between these two calls. There is nothing in file_util that could be used | 79 // between these two calls. There is nothing in file_util that could be used |
79 // to protect against such races, especially as the cache is cross-platform | 80 // to protect against such races, especially as the cache is cross-platform |
80 // and therefore cannot use any POSIX-only tricks. | 81 // and therefore cannot use any POSIX-only tricks. |
81 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) && | 82 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) && |
82 base::DeleteFile(subkey_path, false) && | 83 base::DeleteFile(subkey_path, false) && |
83 file_util::WriteFile(subkey_path, data.data(), data.size()); | 84 file_util::WriteFile(subkey_path, data.data(), data.size()); |
84 } | 85 } |
85 | 86 |
86 bool ResourceCache::Load(const std::string& key, | 87 bool ResourceCache::Load(const std::string& key, |
87 const std::string& subkey, | 88 const std::string& subkey, |
88 std::string* data) { | 89 std::string* data) { |
89 DCHECK(CalledOnValidThread()); | 90 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
90 base::FilePath subkey_path; | 91 base::FilePath subkey_path; |
91 // Only read from |subkey_path| if it is not a symlink. | 92 // Only read from |subkey_path| if it is not a symlink. |
92 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) || | 93 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) || |
93 file_util::IsLink(subkey_path)) { | 94 file_util::IsLink(subkey_path)) { |
94 return false; | 95 return false; |
95 } | 96 } |
96 data->clear(); | 97 data->clear(); |
97 return base::ReadFileToString(subkey_path, data); | 98 return base::ReadFileToString(subkey_path, data); |
98 } | 99 } |
99 | 100 |
100 void ResourceCache::LoadAllSubkeys( | 101 void ResourceCache::LoadAllSubkeys( |
101 const std::string& key, | 102 const std::string& key, |
102 std::map<std::string, std::string>* contents) { | 103 std::map<std::string, std::string>* contents) { |
103 DCHECK(CalledOnValidThread()); | 104 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
104 contents->clear(); | 105 contents->clear(); |
105 base::FilePath key_path; | 106 base::FilePath key_path; |
106 if (!VerifyKeyPath(key, false, &key_path)) | 107 if (!VerifyKeyPath(key, false, &key_path)) |
107 return; | 108 return; |
108 | 109 |
109 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); | 110 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); |
110 for (base::FilePath path = enumerator.Next(); !path.empty(); | 111 for (base::FilePath path = enumerator.Next(); !path.empty(); |
111 path = enumerator.Next()) { | 112 path = enumerator.Next()) { |
112 const std::string encoded_subkey = path.BaseName().MaybeAsASCII(); | 113 const std::string encoded_subkey = path.BaseName().MaybeAsASCII(); |
113 std::string subkey; | 114 std::string subkey; |
114 std::string data; | 115 std::string data; |
115 // Only read from |subkey_path| if it is not a symlink and its name is | 116 // Only read from |subkey_path| if it is not a symlink and its name is |
116 // a base64-encoded string. | 117 // a base64-encoded string. |
117 if (!file_util::IsLink(path) && | 118 if (!file_util::IsLink(path) && |
118 Base64Decode(encoded_subkey, &subkey) && | 119 Base64Decode(encoded_subkey, &subkey) && |
119 base::ReadFileToString(path, &data)) { | 120 base::ReadFileToString(path, &data)) { |
120 (*contents)[subkey].swap(data); | 121 (*contents)[subkey].swap(data); |
121 } | 122 } |
122 } | 123 } |
123 } | 124 } |
124 | 125 |
125 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { | 126 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { |
126 DCHECK(CalledOnValidThread()); | 127 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
127 base::FilePath subkey_path; | 128 base::FilePath subkey_path; |
128 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path)) | 129 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path)) |
129 base::DeleteFile(subkey_path, false); | 130 base::DeleteFile(subkey_path, false); |
130 // Delete() does nothing if the directory given to it is not empty. Hence, the | 131 // Delete() does nothing if the directory given to it is not empty. Hence, the |
131 // call below deletes the directory representing |key| if its last subkey was | 132 // call below deletes the directory representing |key| if its last subkey was |
132 // just removed and does nothing otherwise. | 133 // just removed and does nothing otherwise. |
133 base::DeleteFile(subkey_path.DirName(), false); | 134 base::DeleteFile(subkey_path.DirName(), false); |
134 } | 135 } |
135 | 136 |
136 void ResourceCache::PurgeOtherKeys(const std::set<std::string>& keys_to_keep) { | 137 void ResourceCache::PurgeOtherKeys(const std::set<std::string>& keys_to_keep) { |
137 DCHECK(CalledOnValidThread()); | 138 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
138 std::set<std::string> encoded_keys_to_keep; | 139 std::set<std::string> encoded_keys_to_keep; |
139 if (!Base64Encode(keys_to_keep, &encoded_keys_to_keep)) | 140 if (!Base64Encode(keys_to_keep, &encoded_keys_to_keep)) |
140 return; | 141 return; |
141 | 142 |
142 base::FileEnumerator enumerator( | 143 base::FileEnumerator enumerator( |
143 cache_dir_, false, base::FileEnumerator::DIRECTORIES); | 144 cache_dir_, false, base::FileEnumerator::DIRECTORIES); |
144 for (base::FilePath path = enumerator.Next(); !path.empty(); | 145 for (base::FilePath path = enumerator.Next(); !path.empty(); |
145 path = enumerator.Next()) { | 146 path = enumerator.Next()) { |
146 const std::string name(path.BaseName().MaybeAsASCII()); | 147 const std::string name(path.BaseName().MaybeAsASCII()); |
147 if (encoded_keys_to_keep.find(name) == encoded_keys_to_keep.end()) | 148 if (encoded_keys_to_keep.find(name) == encoded_keys_to_keep.end()) |
148 base::DeleteFile(path, true); | 149 base::DeleteFile(path, true); |
149 } | 150 } |
150 } | 151 } |
151 | 152 |
152 void ResourceCache::PurgeOtherSubkeys( | 153 void ResourceCache::PurgeOtherSubkeys( |
153 const std::string& key, | 154 const std::string& key, |
154 const std::set<std::string>& subkeys_to_keep) { | 155 const std::set<std::string>& subkeys_to_keep) { |
155 DCHECK(CalledOnValidThread()); | 156 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
156 base::FilePath key_path; | 157 base::FilePath key_path; |
157 if (!VerifyKeyPath(key, false, &key_path)) | 158 if (!VerifyKeyPath(key, false, &key_path)) |
158 return; | 159 return; |
159 | 160 |
160 std::set<std::string> encoded_subkeys_to_keep; | 161 std::set<std::string> encoded_subkeys_to_keep; |
161 if (!Base64Encode(subkeys_to_keep, &encoded_subkeys_to_keep)) | 162 if (!Base64Encode(subkeys_to_keep, &encoded_subkeys_to_keep)) |
162 return; | 163 return; |
163 | 164 |
164 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); | 165 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); |
165 for (base::FilePath path = enumerator.Next(); !path.empty(); | 166 for (base::FilePath path = enumerator.Next(); !path.empty(); |
(...skipping 28 matching lines...) Expand all Loading... |
194 if (!VerifyKeyPath(key, allow_create_key, &key_path) || | 195 if (!VerifyKeyPath(key, allow_create_key, &key_path) || |
195 !Base64Encode(subkey, &encoded)) { | 196 !Base64Encode(subkey, &encoded)) { |
196 return false; | 197 return false; |
197 } | 198 } |
198 *path = key_path.AppendASCII(encoded); | 199 *path = key_path.AppendASCII(encoded); |
199 return true; | 200 return true; |
200 } | 201 } |
201 | 202 |
202 | 203 |
203 } // namespace policy | 204 } // namespace policy |
OLD | NEW |