| 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 "components/policy/core/common/cloud/resource_cache.h" | 5 #include "components/policy/core/common/cloud/resource_cache.h" |
| 6 | 6 |
| 7 #include "base/base64url.h" | 7 #include "base/base64url.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/files/file_enumerator.h" | 9 #include "base/files/file_enumerator.h" |
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 } // namespace | 40 } // namespace |
| 41 | 41 |
| 42 ResourceCache::ResourceCache( | 42 ResourceCache::ResourceCache( |
| 43 const base::FilePath& cache_dir, | 43 const base::FilePath& cache_dir, |
| 44 scoped_refptr<base::SequencedTaskRunner> task_runner) | 44 scoped_refptr<base::SequencedTaskRunner> task_runner) |
| 45 : cache_dir_(cache_dir), | 45 : cache_dir_(cache_dir), |
| 46 task_runner_(task_runner) { | 46 task_runner_(task_runner) { |
| 47 } | 47 } |
| 48 | 48 |
| 49 ResourceCache::~ResourceCache() { | 49 ResourceCache::~ResourceCache() { |
| 50 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 50 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 51 } | 51 } |
| 52 | 52 |
| 53 bool ResourceCache::Store(const std::string& key, | 53 bool ResourceCache::Store(const std::string& key, |
| 54 const std::string& subkey, | 54 const std::string& subkey, |
| 55 const std::string& data) { | 55 const std::string& data) { |
| 56 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 56 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 57 base::FilePath subkey_path; | 57 base::FilePath subkey_path; |
| 58 // Delete the file before writing to it. This ensures that the write does not | 58 // Delete the file before writing to it. This ensures that the write does not |
| 59 // follow a symlink planted at |subkey_path|, clobbering a file outside the | 59 // follow a symlink planted at |subkey_path|, clobbering a file outside the |
| 60 // cache directory. The mechanism is meant to foil file-system-level attacks | 60 // cache directory. The mechanism is meant to foil file-system-level attacks |
| 61 // where a symlink is planted in the cache directory before Chrome has | 61 // where a symlink is planted in the cache directory before Chrome has |
| 62 // started. An attacker controlling a process running concurrently with Chrome | 62 // started. An attacker controlling a process running concurrently with Chrome |
| 63 // would be able to race against the protection by re-creating the symlink | 63 // would be able to race against the protection by re-creating the symlink |
| 64 // between these two calls. There is nothing in file_util that could be used | 64 // between these two calls. There is nothing in file_util that could be used |
| 65 // to protect against such races, especially as the cache is cross-platform | 65 // to protect against such races, especially as the cache is cross-platform |
| 66 // and therefore cannot use any POSIX-only tricks. | 66 // and therefore cannot use any POSIX-only tricks. |
| 67 int size = base::checked_cast<int>(data.size()); | 67 int size = base::checked_cast<int>(data.size()); |
| 68 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) && | 68 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) && |
| 69 base::DeleteFile(subkey_path, false) && | 69 base::DeleteFile(subkey_path, false) && |
| 70 (base::WriteFile(subkey_path, data.data(), size) == size); | 70 (base::WriteFile(subkey_path, data.data(), size) == size); |
| 71 } | 71 } |
| 72 | 72 |
| 73 bool ResourceCache::Load(const std::string& key, | 73 bool ResourceCache::Load(const std::string& key, |
| 74 const std::string& subkey, | 74 const std::string& subkey, |
| 75 std::string* data) { | 75 std::string* data) { |
| 76 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 76 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 77 base::FilePath subkey_path; | 77 base::FilePath subkey_path; |
| 78 // Only read from |subkey_path| if it is not a symlink. | 78 // Only read from |subkey_path| if it is not a symlink. |
| 79 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) || | 79 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) || |
| 80 base::IsLink(subkey_path)) { | 80 base::IsLink(subkey_path)) { |
| 81 return false; | 81 return false; |
| 82 } | 82 } |
| 83 data->clear(); | 83 data->clear(); |
| 84 return base::ReadFileToString(subkey_path, data); | 84 return base::ReadFileToString(subkey_path, data); |
| 85 } | 85 } |
| 86 | 86 |
| 87 void ResourceCache::LoadAllSubkeys( | 87 void ResourceCache::LoadAllSubkeys( |
| 88 const std::string& key, | 88 const std::string& key, |
| 89 std::map<std::string, std::string>* contents) { | 89 std::map<std::string, std::string>* contents) { |
| 90 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 90 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 91 contents->clear(); | 91 contents->clear(); |
| 92 base::FilePath key_path; | 92 base::FilePath key_path; |
| 93 if (!VerifyKeyPath(key, false, &key_path)) | 93 if (!VerifyKeyPath(key, false, &key_path)) |
| 94 return; | 94 return; |
| 95 | 95 |
| 96 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); | 96 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); |
| 97 for (base::FilePath path = enumerator.Next(); !path.empty(); | 97 for (base::FilePath path = enumerator.Next(); !path.empty(); |
| 98 path = enumerator.Next()) { | 98 path = enumerator.Next()) { |
| 99 const std::string encoded_subkey = path.BaseName().MaybeAsASCII(); | 99 const std::string encoded_subkey = path.BaseName().MaybeAsASCII(); |
| 100 std::string subkey; | 100 std::string subkey; |
| 101 std::string data; | 101 std::string data; |
| 102 // Only read from |subkey_path| if it is not a symlink and its name is | 102 // Only read from |subkey_path| if it is not a symlink and its name is |
| 103 // a base64-encoded string. | 103 // a base64-encoded string. |
| 104 if (!base::IsLink(path) && | 104 if (!base::IsLink(path) && |
| 105 base::Base64UrlDecode(encoded_subkey, | 105 base::Base64UrlDecode(encoded_subkey, |
| 106 base::Base64UrlDecodePolicy::REQUIRE_PADDING, | 106 base::Base64UrlDecodePolicy::REQUIRE_PADDING, |
| 107 &subkey) && | 107 &subkey) && |
| 108 !subkey.empty() && base::ReadFileToString(path, &data)) { | 108 !subkey.empty() && base::ReadFileToString(path, &data)) { |
| 109 (*contents)[subkey].swap(data); | 109 (*contents)[subkey].swap(data); |
| 110 } | 110 } |
| 111 } | 111 } |
| 112 } | 112 } |
| 113 | 113 |
| 114 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { | 114 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { |
| 115 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 115 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 116 base::FilePath subkey_path; | 116 base::FilePath subkey_path; |
| 117 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path)) | 117 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path)) |
| 118 base::DeleteFile(subkey_path, false); | 118 base::DeleteFile(subkey_path, false); |
| 119 // Delete() does nothing if the directory given to it is not empty. Hence, the | 119 // Delete() does nothing if the directory given to it is not empty. Hence, the |
| 120 // call below deletes the directory representing |key| if its last subkey was | 120 // call below deletes the directory representing |key| if its last subkey was |
| 121 // just removed and does nothing otherwise. | 121 // just removed and does nothing otherwise. |
| 122 base::DeleteFile(subkey_path.DirName(), false); | 122 base::DeleteFile(subkey_path.DirName(), false); |
| 123 } | 123 } |
| 124 | 124 |
| 125 void ResourceCache::Clear(const std::string& key) { | 125 void ResourceCache::Clear(const std::string& key) { |
| 126 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 126 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 127 base::FilePath key_path; | 127 base::FilePath key_path; |
| 128 if (VerifyKeyPath(key, false, &key_path)) | 128 if (VerifyKeyPath(key, false, &key_path)) |
| 129 base::DeleteFile(key_path, true); | 129 base::DeleteFile(key_path, true); |
| 130 } | 130 } |
| 131 | 131 |
| 132 void ResourceCache::FilterSubkeys(const std::string& key, | 132 void ResourceCache::FilterSubkeys(const std::string& key, |
| 133 const SubkeyFilter& test) { | 133 const SubkeyFilter& test) { |
| 134 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 134 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 135 | 135 |
| 136 base::FilePath key_path; | 136 base::FilePath key_path; |
| 137 if (!VerifyKeyPath(key, false, &key_path)) | 137 if (!VerifyKeyPath(key, false, &key_path)) |
| 138 return; | 138 return; |
| 139 | 139 |
| 140 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); | 140 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); |
| 141 for (base::FilePath subkey_path = enumerator.Next(); | 141 for (base::FilePath subkey_path = enumerator.Next(); |
| 142 !subkey_path.empty(); subkey_path = enumerator.Next()) { | 142 !subkey_path.empty(); subkey_path = enumerator.Next()) { |
| 143 std::string subkey; | 143 std::string subkey; |
| 144 // Delete files with invalid names, and files whose subkey doesn't pass the | 144 // Delete files with invalid names, and files whose subkey doesn't pass the |
| 145 // filter. | 145 // filter. |
| 146 if (!base::Base64UrlDecode(subkey_path.BaseName().MaybeAsASCII(), | 146 if (!base::Base64UrlDecode(subkey_path.BaseName().MaybeAsASCII(), |
| 147 base::Base64UrlDecodePolicy::REQUIRE_PADDING, | 147 base::Base64UrlDecodePolicy::REQUIRE_PADDING, |
| 148 &subkey) || | 148 &subkey) || |
| 149 subkey.empty() || test.Run(subkey)) { | 149 subkey.empty() || test.Run(subkey)) { |
| 150 base::DeleteFile(subkey_path, true); | 150 base::DeleteFile(subkey_path, true); |
| 151 } | 151 } |
| 152 } | 152 } |
| 153 | 153 |
| 154 // Delete() does nothing if the directory given to it is not empty. Hence, the | 154 // Delete() does nothing if the directory given to it is not empty. Hence, the |
| 155 // call below deletes the directory representing |key| if all of its subkeys | 155 // call below deletes the directory representing |key| if all of its subkeys |
| 156 // were just removed and does nothing otherwise. | 156 // were just removed and does nothing otherwise. |
| 157 base::DeleteFile(key_path, false); | 157 base::DeleteFile(key_path, false); |
| 158 } | 158 } |
| 159 | 159 |
| 160 void ResourceCache::PurgeOtherKeys(const std::set<std::string>& keys_to_keep) { | 160 void ResourceCache::PurgeOtherKeys(const std::set<std::string>& keys_to_keep) { |
| 161 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 161 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 162 std::set<std::string> encoded_keys_to_keep; | 162 std::set<std::string> encoded_keys_to_keep; |
| 163 if (!Base64UrlEncode(keys_to_keep, &encoded_keys_to_keep)) | 163 if (!Base64UrlEncode(keys_to_keep, &encoded_keys_to_keep)) |
| 164 return; | 164 return; |
| 165 | 165 |
| 166 base::FileEnumerator enumerator( | 166 base::FileEnumerator enumerator( |
| 167 cache_dir_, false, base::FileEnumerator::DIRECTORIES); | 167 cache_dir_, false, base::FileEnumerator::DIRECTORIES); |
| 168 for (base::FilePath path = enumerator.Next(); !path.empty(); | 168 for (base::FilePath path = enumerator.Next(); !path.empty(); |
| 169 path = enumerator.Next()) { | 169 path = enumerator.Next()) { |
| 170 const std::string name(path.BaseName().MaybeAsASCII()); | 170 const std::string name(path.BaseName().MaybeAsASCII()); |
| 171 if (encoded_keys_to_keep.find(name) == encoded_keys_to_keep.end()) | 171 if (encoded_keys_to_keep.find(name) == encoded_keys_to_keep.end()) |
| 172 base::DeleteFile(path, true); | 172 base::DeleteFile(path, true); |
| 173 } | 173 } |
| 174 } | 174 } |
| 175 | 175 |
| 176 void ResourceCache::PurgeOtherSubkeys( | 176 void ResourceCache::PurgeOtherSubkeys( |
| 177 const std::string& key, | 177 const std::string& key, |
| 178 const std::set<std::string>& subkeys_to_keep) { | 178 const std::set<std::string>& subkeys_to_keep) { |
| 179 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 179 DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 180 base::FilePath key_path; | 180 base::FilePath key_path; |
| 181 if (!VerifyKeyPath(key, false, &key_path)) | 181 if (!VerifyKeyPath(key, false, &key_path)) |
| 182 return; | 182 return; |
| 183 | 183 |
| 184 std::set<std::string> encoded_subkeys_to_keep; | 184 std::set<std::string> encoded_subkeys_to_keep; |
| 185 if (!Base64UrlEncode(subkeys_to_keep, &encoded_subkeys_to_keep)) | 185 if (!Base64UrlEncode(subkeys_to_keep, &encoded_subkeys_to_keep)) |
| 186 return; | 186 return; |
| 187 | 187 |
| 188 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); | 188 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); |
| 189 for (base::FilePath path = enumerator.Next(); !path.empty(); | 189 for (base::FilePath path = enumerator.Next(); !path.empty(); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 std::string encoded; | 231 std::string encoded; |
| 232 base::Base64UrlEncode(subkey, base::Base64UrlEncodePolicy::INCLUDE_PADDING, | 232 base::Base64UrlEncode(subkey, base::Base64UrlEncodePolicy::INCLUDE_PADDING, |
| 233 &encoded); | 233 &encoded); |
| 234 | 234 |
| 235 *path = key_path.AppendASCII(encoded); | 235 *path = key_path.AppendASCII(encoded); |
| 236 return true; | 236 return true; |
| 237 } | 237 } |
| 238 | 238 |
| 239 | 239 |
| 240 } // namespace policy | 240 } // namespace policy |
| OLD | NEW |