blob: 827406a675c78cc62d28e753bb8e1bd318b33765 [file] [log] [blame]
Darin Petkovc529c832012-04-18 14:59:42 +02001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Darin Petkov083047b2011-06-23 20:42:48 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/key_file_store.h"
6
Paul Stewart5b9ec982013-01-18 14:12:14 -08007#include <map>
8
Peter Qiu3e7aff52014-03-26 13:03:40 -07009#include <base/files/important_file_writer.h>
Ben Chan11c213f2014-09-05 08:21:06 -070010#include <base/files/file_util.h>
Ben Chana0ddf462014-02-06 11:32:42 -080011#include <base/strings/string_number_conversions.h>
Gary Morain96970242012-04-20 10:59:58 -070012#include <fcntl.h>
13#include <sys/stat.h>
14#include <sys/types.h>
Paul Stewart79963642013-06-12 15:44:17 -070015#include <unistd.h>
Darin Petkov083047b2011-06-23 20:42:48 -070016
Paul Stewart5b9ec982013-01-18 14:12:14 -080017#include "shill/key_value_store.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070018#include "shill/logging.h"
mukesh agrawal3ff527c2014-04-08 17:07:56 -070019#include "shill/scoped_umask.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070020
Paul Stewart5b9ec982013-01-18 14:12:14 -080021using std::map;
Darin Petkov083047b2011-06-23 20:42:48 -070022using std::set;
23using std::string;
Darin Petkovb2841fd2011-06-30 12:54:12 -070024using std::vector;
Darin Petkov083047b2011-06-23 20:42:48 -070025
26namespace shill {
27
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070028namespace Logging {
29static auto kModuleLogScope = ScopeLogger::kStorage;
Paul Stewart8ae18742015-06-16 13:13:10 -070030static string ObjectID(const KeyFileStore* k) { return "(key_file_store)"; }
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070031}
32
Paul Stewart2ebc16d2012-08-23 10:38:39 -070033const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
34
Paul Stewart8ae18742015-06-16 13:13:10 -070035KeyFileStore::KeyFileStore(GLib* glib)
Darin Petkov86964e02011-06-29 13:49:28 -070036 : glib_(glib),
37 crypto_(glib),
Ben Chancc225ef2014-09-30 13:26:51 -070038 key_file_(nullptr) {}
Darin Petkov083047b2011-06-23 20:42:48 -070039
40KeyFileStore::~KeyFileStore() {
41 ReleaseKeyFile();
42}
43
44void KeyFileStore::ReleaseKeyFile() {
45 if (key_file_) {
46 glib_->KeyFileFree(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -070047 key_file_ = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -070048 }
49}
50
Paul Stewart0756db92012-01-27 08:34:47 -080051bool KeyFileStore::IsNonEmpty() const {
Ben Chan7fab8972014-08-10 17:14:46 -070052 int64_t file_size = 0;
Ben Chana0ddf462014-02-06 11:32:42 -080053 return base::GetFileSize(path_, &file_size) && file_size != 0;
Paul Stewart5dc40aa2011-10-28 19:43:43 -070054}
55
Darin Petkov083047b2011-06-23 20:42:48 -070056bool KeyFileStore::Open() {
57 CHECK(!path_.empty());
58 CHECK(!key_file_);
Darin Petkov86964e02011-06-29 13:49:28 -070059 crypto_.Init();
Darin Petkov083047b2011-06-23 20:42:48 -070060 key_file_ = glib_->KeyFileNew();
Paul Stewart5dc40aa2011-10-28 19:43:43 -070061 if (!IsNonEmpty()) {
Darin Petkov083047b2011-06-23 20:42:48 -070062 LOG(INFO) << "Creating a new key file at " << path_.value();
63 return true;
64 }
Paul Stewart8ae18742015-06-16 13:13:10 -070065 GError* error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -070066 if (glib_->KeyFileLoadFromFile(
67 key_file_,
68 path_.value().c_str(),
69 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
70 G_KEY_FILE_KEEP_TRANSLATIONS),
71 &error)) {
72 return true;
73 }
74 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
75 << glib_->ConvertErrorToMessage(error);
76 ReleaseKeyFile();
77 return false;
78}
79
80bool KeyFileStore::Close() {
Chris Masoneb9c00592011-10-06 13:10:39 -070081 bool success = Flush();
82 ReleaseKeyFile();
83 return success;
84}
85
86bool KeyFileStore::Flush() {
Darin Petkov083047b2011-06-23 20:42:48 -070087 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -070088 GError* error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -070089 gsize length = 0;
Paul Stewart8ae18742015-06-16 13:13:10 -070090 gchar* data = glib_->KeyFileToData(key_file_, &length, &error);
Darin Petkov083047b2011-06-23 20:42:48 -070091
92 bool success = true;
93 if (path_.empty()) {
94 LOG(ERROR) << "Empty key file path.";
95 success = false;
96 }
97 if (success && (!data || error)) {
98 LOG(ERROR) << "Failed to convert key file to string: "
99 << glib_->ConvertErrorToMessage(error);
100 success = false;
101 }
mukesh agrawalf60e4062011-05-27 13:13:41 -0700102 if (success) {
mukesh agrawal3ff527c2014-04-08 17:07:56 -0700103 ScopedUmask owner_only_umask(~(S_IRUSR | S_IWUSR));
Peter Qiu3e7aff52014-03-26 13:03:40 -0700104 success = base::ImportantFileWriter::WriteFileAtomically(path_, data);
Peter Qiu3e7aff52014-03-26 13:03:40 -0700105 if (!success) {
106 LOG(ERROR) << "Failed to store key file: " << path_.value();
mukesh agrawalf60e4062011-05-27 13:13:41 -0700107 }
Darin Petkov083047b2011-06-23 20:42:48 -0700108 }
109 glib_->Free(data);
110 return success;
111}
112
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700113bool KeyFileStore::MarkAsCorrupted() {
114 LOG(INFO) << "In " << __func__ << " for " << path_.value();
115 if (path_.empty()) {
116 LOG(ERROR) << "Empty key file path.";
117 return false;
118 }
119 string corrupted_path = path_.value() + kCorruptSuffix;
120 int ret = rename(path_.value().c_str(), corrupted_path.c_str());
121 if (ret != 0) {
122 PLOG(ERROR) << "File rename failed";
123 return false;
124 }
125 return true;
126}
127
Paul Stewart0756db92012-01-27 08:34:47 -0800128set<string> KeyFileStore::GetGroups() const {
Darin Petkov083047b2011-06-23 20:42:48 -0700129 CHECK(key_file_);
130 gsize length = 0;
Paul Stewart8ae18742015-06-16 13:13:10 -0700131 gchar** groups = glib_->KeyFileGetGroups(key_file_, &length);
Darin Petkov083047b2011-06-23 20:42:48 -0700132 if (!groups) {
133 LOG(ERROR) << "Unable to obtain groups.";
134 return set<string>();
135 }
136 set<string> group_set(groups, groups + length);
137 glib_->Strfreev(groups);
138 return group_set;
139}
140
Paul Stewarta41e38d2011-11-11 07:47:29 -0800141// Returns a set so that caller can easily test whether a particular group
142// is contained within this collection.
Paul Stewart8ae18742015-06-16 13:13:10 -0700143set<string> KeyFileStore::GetGroupsWithKey(const string& key) const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800144 set<string> groups = GetGroups();
145 set<string> groups_with_key;
Paul Stewart8ae18742015-06-16 13:13:10 -0700146 for (const auto& group : groups) {
Ben Chancc225ef2014-09-30 13:26:51 -0700147 if (glib_->KeyFileHasKey(key_file_, group.c_str(), key.c_str(), nullptr)) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700148 groups_with_key.insert(group);
Paul Stewarta41e38d2011-11-11 07:47:29 -0800149 }
150 }
151 return groups_with_key;
152}
153
Paul Stewart5b9ec982013-01-18 14:12:14 -0800154set<string> KeyFileStore::GetGroupsWithProperties(
Paul Stewart8ae18742015-06-16 13:13:10 -0700155 const KeyValueStore& properties) const {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800156 set<string> groups = GetGroups();
157 set<string> groups_with_properties;
Paul Stewart8ae18742015-06-16 13:13:10 -0700158 for (const auto& group : groups) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700159 if (DoesGroupMatchProperties(group, properties)) {
160 groups_with_properties.insert(group);
Paul Stewart5b9ec982013-01-18 14:12:14 -0800161 }
162 }
163 return groups_with_properties;
164}
165
Paul Stewart8ae18742015-06-16 13:13:10 -0700166bool KeyFileStore::ContainsGroup(const string& group) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700167 CHECK(key_file_);
168 return glib_->KeyFileHasGroup(key_file_, group.c_str());
169}
170
Paul Stewart8ae18742015-06-16 13:13:10 -0700171bool KeyFileStore::DeleteKey(const string& group, const string& key) {
Darin Petkov083047b2011-06-23 20:42:48 -0700172 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -0700173 GError* error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700174 glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700175 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
Darin Petkov083047b2011-06-23 20:42:48 -0700176 LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
177 << glib_->ConvertErrorToMessage(error);
178 return false;
179 }
180 return true;
181}
182
Paul Stewart8ae18742015-06-16 13:13:10 -0700183bool KeyFileStore::DeleteGroup(const string& group) {
Darin Petkov083047b2011-06-23 20:42:48 -0700184 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -0700185 GError* error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700186 glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700187 if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
Darin Petkov86964e02011-06-29 13:49:28 -0700188 LOG(ERROR) << "Failed to delete group " << group << ": "
Darin Petkov083047b2011-06-23 20:42:48 -0700189 << glib_->ConvertErrorToMessage(error);
190 return false;
191 }
192 return true;
193}
194
Paul Stewart8ae18742015-06-16 13:13:10 -0700195bool KeyFileStore::SetHeader(const string& header) {
196 GError* error = nullptr;
Ben Chancc225ef2014-09-30 13:26:51 -0700197 glib_->KeyFileSetComment(key_file_, nullptr, nullptr, header.c_str(), &error);
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700198 if (error) {
199 LOG(ERROR) << "Failed to to set header: "
200 << glib_->ConvertErrorToMessage(error);
201 return false;
202 }
203 return true;
204}
205
Paul Stewart8ae18742015-06-16 13:13:10 -0700206bool KeyFileStore::GetString(const string& group,
207 const string& key,
208 string* value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700209 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -0700210 GError* error = nullptr;
211 gchar* data =
Darin Petkov083047b2011-06-23 20:42:48 -0700212 glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error);
213 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200214 string s = glib_->ConvertErrorToMessage(error);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700215 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700216 return false;
217 }
218 if (value) {
219 *value = data;
220 }
221 glib_->Free(data);
222 return true;
223}
224
Paul Stewart8ae18742015-06-16 13:13:10 -0700225bool KeyFileStore::SetString(const string& group,
226 const string& key,
227 const string& value) {
Darin Petkov083047b2011-06-23 20:42:48 -0700228 CHECK(key_file_);
229 glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
230 return true;
231}
232
Paul Stewart8ae18742015-06-16 13:13:10 -0700233bool KeyFileStore::GetBool(const string& group,
234 const string& key,
235 bool* value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700236 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -0700237 GError* error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700238 gboolean data =
239 glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
240 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200241 string s = glib_->ConvertErrorToMessage(error);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700242 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700243 return false;
244 }
245 if (value) {
246 *value = data;
247 }
248 return true;
249}
250
Paul Stewart8ae18742015-06-16 13:13:10 -0700251bool KeyFileStore::SetBool(const string& group, const string& key, bool value) {
Darin Petkov083047b2011-06-23 20:42:48 -0700252 CHECK(key_file_);
253 glib_->KeyFileSetBoolean(key_file_,
254 group.c_str(),
255 key.c_str(),
256 value ? TRUE : FALSE);
257 return true;
258}
259
Paul Stewart0756db92012-01-27 08:34:47 -0800260bool KeyFileStore::GetInt(
Paul Stewart8ae18742015-06-16 13:13:10 -0700261 const string& group, const string& key, int* value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700262 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -0700263 GError* error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700264 gint data =
265 glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error);
266 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200267 string s = glib_->ConvertErrorToMessage(error);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700268 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700269 return false;
270 }
271 if (value) {
272 *value = data;
273 }
274 return true;
275}
276
Paul Stewart8ae18742015-06-16 13:13:10 -0700277bool KeyFileStore::SetInt(const string& group, const string& key, int value) {
Darin Petkov083047b2011-06-23 20:42:48 -0700278 CHECK(key_file_);
279 glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
280 return true;
281}
282
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700283bool KeyFileStore::GetUint64(
Paul Stewart8ae18742015-06-16 13:13:10 -0700284 const string& group, const string& key, uint64_t* value) const {
Ben Chan7fab8972014-08-10 17:14:46 -0700285 // Read the value in as a string and then convert to uint64_t because glib's
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700286 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
287 // in unit tests.
288 string data_string;
289 if (!GetString(group, key, &data_string)) {
290 return false;
291 }
292
Ben Chan7fab8972014-08-10 17:14:46 -0700293 uint64_t data;
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700294 if (!base::StringToUint64(data_string, &data)) {
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700295 SLOG(this, 10) << "Failed to convert (" << group << ":" << key << "): "
296 << "string to uint64_t conversion failed";
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700297 return false;
298 }
299
300 if (value) {
301 *value = data;
302 }
303
304 return true;
305}
306
307bool KeyFileStore::SetUint64(
Paul Stewart8ae18742015-06-16 13:13:10 -0700308 const string& group, const string& key, uint64_t value) {
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700309 // Convert the value to a string first, then save the value because glib's
310 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
311 // unit tests.
312 return SetString(group, key, base::Uint64ToString(value));
313}
314
Paul Stewart8ae18742015-06-16 13:13:10 -0700315bool KeyFileStore::GetStringList(const string& group,
316 const string& key,
317 vector<string>* value) const {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700318 CHECK(key_file_);
319 gsize length = 0;
Paul Stewart8ae18742015-06-16 13:13:10 -0700320 GError* error = nullptr;
321 gchar** data = glib_->KeyFileGetStringList(key_file_,
Darin Petkovb2841fd2011-06-30 12:54:12 -0700322 group.c_str(),
323 key.c_str(),
324 &length,
325 &error);
326 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200327 string s = glib_->ConvertErrorToMessage(error);
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -0700328 SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700329 return false;
330 }
331 if (value) {
332 value->assign(data, data + length);
333 }
334 glib_->Strfreev(data);
335 return true;
336}
337
Paul Stewart8ae18742015-06-16 13:13:10 -0700338bool KeyFileStore::SetStringList(const string& group,
339 const string& key,
340 const vector<string>& value) {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700341 CHECK(key_file_);
Paul Stewart8ae18742015-06-16 13:13:10 -0700342 vector<const char*> list;
343 for (const auto& string_entry : value) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700344 list.push_back(string_entry.c_str());
Darin Petkovb2841fd2011-06-30 12:54:12 -0700345 }
346 glib_->KeyFileSetStringList(key_file_,
347 group.c_str(),
348 key.c_str(),
349 list.data(),
350 list.size());
351 return true;
352}
353
Paul Stewart8ae18742015-06-16 13:13:10 -0700354bool KeyFileStore::GetCryptedString(const string& group,
355 const string& key,
356 string* value) {
Darin Petkov86964e02011-06-29 13:49:28 -0700357 if (!GetString(group, key, value)) {
358 return false;
359 }
360 if (value) {
361 *value = crypto_.Decrypt(*value);
362 }
363 return true;
364}
365
Paul Stewart8ae18742015-06-16 13:13:10 -0700366bool KeyFileStore::SetCryptedString(const string& group,
367 const string& key,
368 const string& value) {
Darin Petkov86964e02011-06-29 13:49:28 -0700369 return SetString(group, key, crypto_.Encrypt(value));
370}
371
Paul Stewart5b9ec982013-01-18 14:12:14 -0800372bool KeyFileStore::DoesGroupMatchProperties(
Paul Stewart8ae18742015-06-16 13:13:10 -0700373 const string& group, const KeyValueStore& properties) const {
Peter Qiud31fb582015-07-15 14:33:32 -0700374 for (const auto& property : properties.properties()) {
375 if (property.second.GetType() == typeid(bool)) { // NOLINT
376 bool value;
377 if (!GetBool(group, property.first, &value) ||
378 value != property.second.Get<bool>()) {
379 return false;
380 }
381 } else if (property.second.GetType() == typeid(int32_t)) {
382 int value;
383 if (!GetInt(group, property.first, &value) ||
384 value != property.second.Get<int32_t>()) {
385 return false;
386 }
387 } else if (property.second.GetType() == typeid(string)) {
388 string value;
389 if (!GetString(group, property.first, &value) ||
390 value != property.second.Get<string>()) {
391 return false;
392 }
Paul Stewart5b9ec982013-01-18 14:12:14 -0800393 }
394 }
395 return true;
396}
397
Darin Petkov083047b2011-06-23 20:42:48 -0700398} // namespace shill