1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/key_file_store.h"
18
19 #include <map>
20
21 #include <base/files/important_file_writer.h>
22 #include <base/files/file_util.h>
23 #include <base/strings/string_number_conversions.h>
24 #include <base/strings/stringprintf.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include "shill/key_value_store.h"
31 #include "shill/logging.h"
32 #include "shill/scoped_umask.h"
33
34 using std::map;
35 using std::set;
36 using std::string;
37 using std::vector;
38
39 namespace shill {
40
41 namespace Logging {
42 static auto kModuleLogScope = ScopeLogger::kStorage;
ObjectID(const KeyFileStore * k)43 static string ObjectID(const KeyFileStore* k) { return "(key_file_store)"; }
44 }
45
46 namespace {
ConvertErrorToMessage(GError * error)47 string ConvertErrorToMessage(GError* error) {
48 if (!error) {
49 return "Unknown GLib error.";
50 }
51 string message =
52 base::StringPrintf("GError(%d): %s", error->code, error->message);
53 g_error_free(error);
54 return message;
55 }
56 } // namespace
57
58 const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
59
KeyFileStore(const base::FilePath & path)60 KeyFileStore::KeyFileStore(const base::FilePath& path)
61 : crypto_(),
62 key_file_(nullptr),
63 path_(path) {
64 CHECK(!path_.empty());
65 }
66
~KeyFileStore()67 KeyFileStore::~KeyFileStore() {
68 ReleaseKeyFile();
69 }
70
ReleaseKeyFile()71 void KeyFileStore::ReleaseKeyFile() {
72 if (key_file_) {
73 g_key_file_free(key_file_);
74 key_file_ = nullptr;
75 }
76 }
77
IsNonEmpty() const78 bool KeyFileStore::IsNonEmpty() const {
79 int64_t file_size = 0;
80 return base::GetFileSize(path_, &file_size) && file_size != 0;
81 }
82
Open()83 bool KeyFileStore::Open() {
84 CHECK(!key_file_);
85 crypto_.Init();
86 key_file_ = g_key_file_new();
87 if (!IsNonEmpty()) {
88 LOG(INFO) << "Creating a new key file at " << path_.value();
89 return true;
90 }
91 GError* error = nullptr;
92 if (g_key_file_load_from_file(
93 key_file_,
94 path_.value().c_str(),
95 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
96 G_KEY_FILE_KEEP_TRANSLATIONS),
97 &error)) {
98 return true;
99 }
100 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
101 << ConvertErrorToMessage(error);
102 ReleaseKeyFile();
103 return false;
104 }
105
Close()106 bool KeyFileStore::Close() {
107 bool success = Flush();
108 ReleaseKeyFile();
109 return success;
110 }
111
Flush()112 bool KeyFileStore::Flush() {
113 CHECK(key_file_);
114 GError* error = nullptr;
115 gsize length = 0;
116 gchar* data = g_key_file_to_data(key_file_, &length, &error);
117
118 bool success = true;
119 if (!data || error) {
120 LOG(ERROR) << "Failed to convert key file to string: "
121 << ConvertErrorToMessage(error);
122 success = false;
123 }
124 if (success) {
125 ScopedUmask owner_only_umask(~(S_IRUSR | S_IWUSR) & 0777);
126 success = base::ImportantFileWriter::WriteFileAtomically(path_, data);
127 if (!success) {
128 LOG(ERROR) << "Failed to store key file: " << path_.value();
129 }
130 }
131 g_free(data);
132 return success;
133 }
134
MarkAsCorrupted()135 bool KeyFileStore::MarkAsCorrupted() {
136 LOG(INFO) << "In " << __func__ << " for " << path_.value();
137 string corrupted_path = path_.value() + kCorruptSuffix;
138 int ret = rename(path_.value().c_str(), corrupted_path.c_str());
139 if (ret != 0) {
140 PLOG(ERROR) << "File rename failed";
141 return false;
142 }
143 return true;
144 }
145
GetGroups() const146 set<string> KeyFileStore::GetGroups() const {
147 CHECK(key_file_);
148 gsize length = 0;
149 gchar** groups = g_key_file_get_groups(key_file_, &length);
150 if (!groups) {
151 LOG(ERROR) << "Unable to obtain groups.";
152 return set<string>();
153 }
154 set<string> group_set(groups, groups + length);
155 g_strfreev(groups);
156 return group_set;
157 }
158
159 // Returns a set so that caller can easily test whether a particular group
160 // is contained within this collection.
GetGroupsWithKey(const string & key) const161 set<string> KeyFileStore::GetGroupsWithKey(const string& key) const {
162 set<string> groups = GetGroups();
163 set<string> groups_with_key;
164 for (const auto& group : groups) {
165 if (g_key_file_has_key(key_file_, group.c_str(), key.c_str(), nullptr)) {
166 groups_with_key.insert(group);
167 }
168 }
169 return groups_with_key;
170 }
171
GetGroupsWithProperties(const KeyValueStore & properties) const172 set<string> KeyFileStore::GetGroupsWithProperties(
173 const KeyValueStore& properties) const {
174 set<string> groups = GetGroups();
175 set<string> groups_with_properties;
176 for (const auto& group : groups) {
177 if (DoesGroupMatchProperties(group, properties)) {
178 groups_with_properties.insert(group);
179 }
180 }
181 return groups_with_properties;
182 }
183
ContainsGroup(const string & group) const184 bool KeyFileStore::ContainsGroup(const string& group) const {
185 CHECK(key_file_);
186 return g_key_file_has_group(key_file_, group.c_str());
187 }
188
DeleteKey(const string & group,const string & key)189 bool KeyFileStore::DeleteKey(const string& group, const string& key) {
190 CHECK(key_file_);
191 GError* error = nullptr;
192 g_key_file_remove_key(key_file_, group.c_str(), key.c_str(), &error);
193 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
194 LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
195 << ConvertErrorToMessage(error);
196 return false;
197 }
198 return true;
199 }
200
DeleteGroup(const string & group)201 bool KeyFileStore::DeleteGroup(const string& group) {
202 CHECK(key_file_);
203 GError* error = nullptr;
204 g_key_file_remove_group(key_file_, group.c_str(), &error);
205 if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
206 LOG(ERROR) << "Failed to delete group " << group << ": "
207 << ConvertErrorToMessage(error);
208 return false;
209 }
210 return true;
211 }
212
SetHeader(const string & header)213 bool KeyFileStore::SetHeader(const string& header) {
214 GError* error = nullptr;
215 g_key_file_set_comment(key_file_, nullptr, nullptr, header.c_str(), &error);
216 if (error) {
217 LOG(ERROR) << "Failed to to set header: "
218 << ConvertErrorToMessage(error);
219 return false;
220 }
221 return true;
222 }
223
GetString(const string & group,const string & key,string * value) const224 bool KeyFileStore::GetString(const string& group,
225 const string& key,
226 string* value) const {
227 CHECK(key_file_);
228 GError* error = nullptr;
229 gchar* data =
230 g_key_file_get_string(key_file_, group.c_str(), key.c_str(), &error);
231 if (!data) {
232 string s = ConvertErrorToMessage(error);
233 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
234 return false;
235 }
236 if (value) {
237 *value = data;
238 }
239 g_free(data);
240 return true;
241 }
242
SetString(const string & group,const string & key,const string & value)243 bool KeyFileStore::SetString(const string& group,
244 const string& key,
245 const string& value) {
246 CHECK(key_file_);
247 g_key_file_set_string(key_file_, group.c_str(), key.c_str(), value.c_str());
248 return true;
249 }
250
GetBool(const string & group,const string & key,bool * value) const251 bool KeyFileStore::GetBool(const string& group,
252 const string& key,
253 bool* value) const {
254 CHECK(key_file_);
255 GError* error = nullptr;
256 gboolean data =
257 g_key_file_get_boolean(key_file_, group.c_str(), key.c_str(), &error);
258 if (error) {
259 string s = ConvertErrorToMessage(error);
260 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
261 return false;
262 }
263 if (value) {
264 *value = data;
265 }
266 return true;
267 }
268
SetBool(const string & group,const string & key,bool value)269 bool KeyFileStore::SetBool(const string& group, const string& key, bool value) {
270 CHECK(key_file_);
271 g_key_file_set_boolean(key_file_,
272 group.c_str(),
273 key.c_str(),
274 value ? TRUE : FALSE);
275 return true;
276 }
277
GetInt(const string & group,const string & key,int * value) const278 bool KeyFileStore::GetInt(
279 const string& group, const string& key, int* value) const {
280 CHECK(key_file_);
281 GError* error = nullptr;
282 gint data =
283 g_key_file_get_integer(key_file_, group.c_str(), key.c_str(), &error);
284 if (error) {
285 string s = ConvertErrorToMessage(error);
286 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
287 return false;
288 }
289 if (value) {
290 *value = data;
291 }
292 return true;
293 }
294
SetInt(const string & group,const string & key,int value)295 bool KeyFileStore::SetInt(const string& group, const string& key, int value) {
296 CHECK(key_file_);
297 g_key_file_set_integer(key_file_, group.c_str(), key.c_str(), value);
298 return true;
299 }
300
GetUint64(const string & group,const string & key,uint64_t * value) const301 bool KeyFileStore::GetUint64(
302 const string& group, const string& key, uint64_t* value) const {
303 // Read the value in as a string and then convert to uint64_t because glib's
304 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
305 // in unit tests.
306 string data_string;
307 if (!GetString(group, key, &data_string)) {
308 return false;
309 }
310
311 uint64_t data;
312 if (!base::StringToUint64(data_string, &data)) {
313 SLOG(this, 10) << "Failed to convert (" << group << ":" << key << "): "
314 << "string to uint64_t conversion failed";
315 return false;
316 }
317
318 if (value) {
319 *value = data;
320 }
321
322 return true;
323 }
324
SetUint64(const string & group,const string & key,uint64_t value)325 bool KeyFileStore::SetUint64(
326 const string& group, const string& key, uint64_t value) {
327 // Convert the value to a string first, then save the value because glib's
328 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
329 // unit tests.
330 return SetString(group, key, base::Uint64ToString(value));
331 }
332
GetStringList(const string & group,const string & key,vector<string> * value) const333 bool KeyFileStore::GetStringList(const string& group,
334 const string& key,
335 vector<string>* value) const {
336 CHECK(key_file_);
337 gsize length = 0;
338 GError* error = nullptr;
339 gchar** data = g_key_file_get_string_list(key_file_,
340 group.c_str(),
341 key.c_str(),
342 &length,
343 &error);
344 if (!data) {
345 string s = ConvertErrorToMessage(error);
346 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
347 return false;
348 }
349 if (value) {
350 value->assign(data, data + length);
351 }
352 g_strfreev(data);
353 return true;
354 }
355
SetStringList(const string & group,const string & key,const vector<string> & value)356 bool KeyFileStore::SetStringList(const string& group,
357 const string& key,
358 const vector<string>& value) {
359 CHECK(key_file_);
360 vector<const char*> list;
361 for (const auto& string_entry : value) {
362 list.push_back(string_entry.c_str());
363 }
364 g_key_file_set_string_list(key_file_,
365 group.c_str(),
366 key.c_str(),
367 list.data(),
368 list.size());
369 return true;
370 }
371
GetCryptedString(const string & group,const string & key,string * value)372 bool KeyFileStore::GetCryptedString(const string& group,
373 const string& key,
374 string* value) {
375 if (!GetString(group, key, value)) {
376 return false;
377 }
378 if (value) {
379 *value = crypto_.Decrypt(*value);
380 }
381 return true;
382 }
383
SetCryptedString(const string & group,const string & key,const string & value)384 bool KeyFileStore::SetCryptedString(const string& group,
385 const string& key,
386 const string& value) {
387 return SetString(group, key, crypto_.Encrypt(value));
388 }
389
DoesGroupMatchProperties(const string & group,const KeyValueStore & properties) const390 bool KeyFileStore::DoesGroupMatchProperties(
391 const string& group, const KeyValueStore& properties) const {
392 for (const auto& property : properties.properties()) {
393 if (property.second.IsTypeCompatible<bool>()) {
394 bool value;
395 if (!GetBool(group, property.first, &value) ||
396 value != property.second.Get<bool>()) {
397 return false;
398 }
399 } else if (property.second.IsTypeCompatible<int32_t>()) {
400 int value;
401 if (!GetInt(group, property.first, &value) ||
402 value != property.second.Get<int32_t>()) {
403 return false;
404 }
405 } else if (property.second.IsTypeCompatible<string>()) {
406 string value;
407 if (!GetString(group, property.first, &value) ||
408 value != property.second.Get<string>()) {
409 return false;
410 }
411 }
412 }
413 return true;
414 }
415
416 } // namespace shill
417