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" |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 // Delete the file before writing to it. This ensures that the write does not | 55 // Delete the file before writing to it. This ensures that the write does not |
56 // follow a symlink planted at |subkey_path|, clobbering a file outside the | 56 // follow a symlink planted at |subkey_path|, clobbering a file outside the |
57 // cache directory. The mechanism is meant to foil file-system-level attacks | 57 // cache directory. The mechanism is meant to foil file-system-level attacks |
58 // where a symlink is planted in the cache directory before Chrome has | 58 // where a symlink is planted in the cache directory before Chrome has |
59 // started. An attacker controlling a process running concurrently with Chrome | 59 // started. An attacker controlling a process running concurrently with Chrome |
60 // would be able to race against the protection by re-creating the symlink | 60 // would be able to race against the protection by re-creating the symlink |
61 // between these two calls. There is nothing in file_util that could be used | 61 // between these two calls. There is nothing in file_util that could be used |
62 // to protect against such races, especially as the cache is cross-platform | 62 // to protect against such races, especially as the cache is cross-platform |
63 // and therefore cannot use any POSIX-only tricks. | 63 // and therefore cannot use any POSIX-only tricks. |
64 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) && | 64 return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) && |
65 base::Delete(subkey_path, false) && | 65 base::DeleteFile(subkey_path, false) && |
66 file_util::WriteFile(subkey_path, data.data(), data.size()); | 66 file_util::WriteFile(subkey_path, data.data(), data.size()); |
67 } | 67 } |
68 | 68 |
69 bool ResourceCache::Load(const std::string& key, | 69 bool ResourceCache::Load(const std::string& key, |
70 const std::string& subkey, | 70 const std::string& subkey, |
71 std::string* data) { | 71 std::string* data) { |
72 DCHECK(CalledOnValidThread()); | 72 DCHECK(CalledOnValidThread()); |
73 base::FilePath subkey_path; | 73 base::FilePath subkey_path; |
74 // Only read from |subkey_path| if it is not a symlink. | 74 // Only read from |subkey_path| if it is not a symlink. |
75 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) || | 75 if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) || |
(...skipping 26 matching lines...) Expand all Loading... |
102 file_util::ReadFileToString(path, &data)) { | 102 file_util::ReadFileToString(path, &data)) { |
103 (*contents)[subkey].swap(data); | 103 (*contents)[subkey].swap(data); |
104 } | 104 } |
105 } | 105 } |
106 } | 106 } |
107 | 107 |
108 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { | 108 void ResourceCache::Delete(const std::string& key, const std::string& subkey) { |
109 DCHECK(CalledOnValidThread()); | 109 DCHECK(CalledOnValidThread()); |
110 base::FilePath subkey_path; | 110 base::FilePath subkey_path; |
111 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path)) | 111 if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path)) |
112 base::Delete(subkey_path, false); | 112 base::DeleteFile(subkey_path, false); |
113 // Delete() does nothing if the directory given to it is not empty. Hence, the | 113 // Delete() does nothing if the directory given to it is not empty. Hence, the |
114 // call below deletes the directory representing |key| if its last subkey was | 114 // call below deletes the directory representing |key| if its last subkey was |
115 // just removed and does nothing otherwise. | 115 // just removed and does nothing otherwise. |
116 base::Delete(subkey_path.DirName(), false); | 116 base::DeleteFile(subkey_path.DirName(), false); |
117 } | 117 } |
118 | 118 |
119 void ResourceCache::PurgeOtherSubkeys( | 119 void ResourceCache::PurgeOtherSubkeys( |
120 const std::string& key, | 120 const std::string& key, |
121 const std::set<std::string>& subkeys_to_keep) { | 121 const std::set<std::string>& subkeys_to_keep) { |
122 DCHECK(CalledOnValidThread()); | 122 DCHECK(CalledOnValidThread()); |
123 base::FilePath key_path; | 123 base::FilePath key_path; |
124 if (!VerifyKeyPath(key, false, &key_path)) | 124 if (!VerifyKeyPath(key, false, &key_path)) |
125 return; | 125 return; |
126 | 126 |
127 std::set<std::string> encoded_subkeys_to_keep; | 127 std::set<std::string> encoded_subkeys_to_keep; |
128 for (std::set<std::string>::const_iterator it = subkeys_to_keep.begin(); | 128 for (std::set<std::string>::const_iterator it = subkeys_to_keep.begin(); |
129 it != subkeys_to_keep.end(); ++it) { | 129 it != subkeys_to_keep.end(); ++it) { |
130 std::string encoded; | 130 std::string encoded; |
131 if (!Base64Encode(*it, &encoded)) | 131 if (!Base64Encode(*it, &encoded)) |
132 return; | 132 return; |
133 encoded_subkeys_to_keep.insert(encoded); | 133 encoded_subkeys_to_keep.insert(encoded); |
134 } | 134 } |
135 | 135 |
136 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); | 136 base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES); |
137 for (base::FilePath path = enumerator.Next(); !path.empty(); | 137 for (base::FilePath path = enumerator.Next(); !path.empty(); |
138 path = enumerator.Next()) { | 138 path = enumerator.Next()) { |
139 const std::string name(path.BaseName().MaybeAsASCII()); | 139 const std::string name(path.BaseName().MaybeAsASCII()); |
140 if (encoded_subkeys_to_keep.find(name) == encoded_subkeys_to_keep.end()) | 140 if (encoded_subkeys_to_keep.find(name) == encoded_subkeys_to_keep.end()) |
141 base::Delete(path, false); | 141 base::DeleteFile(path, false); |
142 } | 142 } |
143 // Delete() does nothing if the directory given to it is not empty. Hence, the | 143 // Delete() does nothing if the directory given to it is not empty. Hence, the |
144 // call below deletes the directory representing |key| if all of its subkeys | 144 // call below deletes the directory representing |key| if all of its subkeys |
145 // were just removed and does nothing otherwise. | 145 // were just removed and does nothing otherwise. |
146 base::Delete(key_path, false); | 146 base::DeleteFile(key_path, false); |
147 } | 147 } |
148 | 148 |
149 bool ResourceCache::VerifyKeyPath(const std::string& key, | 149 bool ResourceCache::VerifyKeyPath(const std::string& key, |
150 bool allow_create, | 150 bool allow_create, |
151 base::FilePath* path) { | 151 base::FilePath* path) { |
152 std::string encoded; | 152 std::string encoded; |
153 if (!Base64Encode(key, &encoded)) | 153 if (!Base64Encode(key, &encoded)) |
154 return false; | 154 return false; |
155 *path = cache_dir_.AppendASCII(encoded); | 155 *path = cache_dir_.AppendASCII(encoded); |
156 return allow_create ? file_util::CreateDirectory(*path) : | 156 return allow_create ? file_util::CreateDirectory(*path) : |
157 base::DirectoryExists(*path); | 157 base::DirectoryExists(*path); |
158 } | 158 } |
159 | 159 |
160 bool ResourceCache::VerifyKeyPathAndGetSubkeyPath(const std::string& key, | 160 bool ResourceCache::VerifyKeyPathAndGetSubkeyPath(const std::string& key, |
161 bool allow_create_key, | 161 bool allow_create_key, |
162 const std::string& subkey, | 162 const std::string& subkey, |
163 base::FilePath* path) { | 163 base::FilePath* path) { |
164 base::FilePath key_path; | 164 base::FilePath key_path; |
165 std::string encoded; | 165 std::string encoded; |
166 if (!VerifyKeyPath(key, allow_create_key, &key_path) || | 166 if (!VerifyKeyPath(key, allow_create_key, &key_path) || |
167 !Base64Encode(subkey, &encoded)) { | 167 !Base64Encode(subkey, &encoded)) { |
168 return false; | 168 return false; |
169 } | 169 } |
170 *path = key_path.AppendASCII(encoded); | 170 *path = key_path.AppendASCII(encoded); |
171 return true; | 171 return true; |
172 } | 172 } |
173 | 173 |
174 | 174 |
175 } // namespace policy | 175 } // namespace policy |
OLD | NEW |