blob: 970cefe8b039b4a5c343aa15054c950378ec6126 [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
Paul Stewart2ebc16d2012-08-23 10:38:39 -070028const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
29
Darin Petkov86964e02011-06-29 13:49:28 -070030KeyFileStore::KeyFileStore(GLib *glib)
31 : glib_(glib),
32 crypto_(glib),
Ben Chancc225ef2014-09-30 13:26:51 -070033 key_file_(nullptr) {}
Darin Petkov083047b2011-06-23 20:42:48 -070034
35KeyFileStore::~KeyFileStore() {
36 ReleaseKeyFile();
37}
38
39void KeyFileStore::ReleaseKeyFile() {
40 if (key_file_) {
41 glib_->KeyFileFree(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -070042 key_file_ = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -070043 }
44}
45
Paul Stewart0756db92012-01-27 08:34:47 -080046bool KeyFileStore::IsNonEmpty() const {
Ben Chan7fab8972014-08-10 17:14:46 -070047 int64_t file_size = 0;
Ben Chana0ddf462014-02-06 11:32:42 -080048 return base::GetFileSize(path_, &file_size) && file_size != 0;
Paul Stewart5dc40aa2011-10-28 19:43:43 -070049}
50
Darin Petkov083047b2011-06-23 20:42:48 -070051bool KeyFileStore::Open() {
52 CHECK(!path_.empty());
53 CHECK(!key_file_);
Darin Petkov86964e02011-06-29 13:49:28 -070054 crypto_.Init();
Darin Petkov083047b2011-06-23 20:42:48 -070055 key_file_ = glib_->KeyFileNew();
Paul Stewart5dc40aa2011-10-28 19:43:43 -070056 if (!IsNonEmpty()) {
Darin Petkov083047b2011-06-23 20:42:48 -070057 LOG(INFO) << "Creating a new key file at " << path_.value();
58 return true;
59 }
Ben Chancc225ef2014-09-30 13:26:51 -070060 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -070061 if (glib_->KeyFileLoadFromFile(
62 key_file_,
63 path_.value().c_str(),
64 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
65 G_KEY_FILE_KEEP_TRANSLATIONS),
66 &error)) {
67 return true;
68 }
69 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
70 << glib_->ConvertErrorToMessage(error);
71 ReleaseKeyFile();
72 return false;
73}
74
75bool KeyFileStore::Close() {
Chris Masoneb9c00592011-10-06 13:10:39 -070076 bool success = Flush();
77 ReleaseKeyFile();
78 return success;
79}
80
81bool KeyFileStore::Flush() {
Darin Petkov083047b2011-06-23 20:42:48 -070082 CHECK(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -070083 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -070084 gsize length = 0;
85 gchar *data = glib_->KeyFileToData(key_file_, &length, &error);
Darin Petkov083047b2011-06-23 20:42:48 -070086
87 bool success = true;
88 if (path_.empty()) {
89 LOG(ERROR) << "Empty key file path.";
90 success = false;
91 }
92 if (success && (!data || error)) {
93 LOG(ERROR) << "Failed to convert key file to string: "
94 << glib_->ConvertErrorToMessage(error);
95 success = false;
96 }
mukesh agrawalf60e4062011-05-27 13:13:41 -070097 if (success) {
mukesh agrawal3ff527c2014-04-08 17:07:56 -070098 ScopedUmask owner_only_umask(~(S_IRUSR | S_IWUSR));
Peter Qiu3e7aff52014-03-26 13:03:40 -070099 success = base::ImportantFileWriter::WriteFileAtomically(path_, data);
Peter Qiu3e7aff52014-03-26 13:03:40 -0700100 if (!success) {
101 LOG(ERROR) << "Failed to store key file: " << path_.value();
mukesh agrawalf60e4062011-05-27 13:13:41 -0700102 }
Darin Petkov083047b2011-06-23 20:42:48 -0700103 }
104 glib_->Free(data);
105 return success;
106}
107
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700108bool KeyFileStore::MarkAsCorrupted() {
109 LOG(INFO) << "In " << __func__ << " for " << path_.value();
110 if (path_.empty()) {
111 LOG(ERROR) << "Empty key file path.";
112 return false;
113 }
114 string corrupted_path = path_.value() + kCorruptSuffix;
115 int ret = rename(path_.value().c_str(), corrupted_path.c_str());
116 if (ret != 0) {
117 PLOG(ERROR) << "File rename failed";
118 return false;
119 }
120 return true;
121}
122
Paul Stewart0756db92012-01-27 08:34:47 -0800123set<string> KeyFileStore::GetGroups() const {
Darin Petkov083047b2011-06-23 20:42:48 -0700124 CHECK(key_file_);
125 gsize length = 0;
Paul Stewarta41e38d2011-11-11 07:47:29 -0800126 gchar **groups = glib_->KeyFileGetGroups(key_file_, &length);
Darin Petkov083047b2011-06-23 20:42:48 -0700127 if (!groups) {
128 LOG(ERROR) << "Unable to obtain groups.";
129 return set<string>();
130 }
131 set<string> group_set(groups, groups + length);
132 glib_->Strfreev(groups);
133 return group_set;
134}
135
Paul Stewarta41e38d2011-11-11 07:47:29 -0800136// Returns a set so that caller can easily test whether a particular group
137// is contained within this collection.
Paul Stewart0756db92012-01-27 08:34:47 -0800138set<string> KeyFileStore::GetGroupsWithKey(const string &key) const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800139 set<string> groups = GetGroups();
140 set<string> groups_with_key;
Paul Stewart6db7b242014-05-02 15:34:21 -0700141 for (const auto &group : groups) {
Ben Chancc225ef2014-09-30 13:26:51 -0700142 if (glib_->KeyFileHasKey(key_file_, group.c_str(), key.c_str(), nullptr)) {
Paul Stewart6db7b242014-05-02 15:34:21 -0700143 groups_with_key.insert(group);
Paul Stewarta41e38d2011-11-11 07:47:29 -0800144 }
145 }
146 return groups_with_key;
147}
148
Paul Stewart5b9ec982013-01-18 14:12:14 -0800149set<string> KeyFileStore::GetGroupsWithProperties(
150 const KeyValueStore &properties) const {
151 set<string> groups = GetGroups();
152 set<string> groups_with_properties;
Paul Stewart6db7b242014-05-02 15:34:21 -0700153 for (const auto &group : groups) {
154 if (DoesGroupMatchProperties(group, properties)) {
155 groups_with_properties.insert(group);
Paul Stewart5b9ec982013-01-18 14:12:14 -0800156 }
157 }
158 return groups_with_properties;
159}
160
Paul Stewart0756db92012-01-27 08:34:47 -0800161bool KeyFileStore::ContainsGroup(const string &group) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700162 CHECK(key_file_);
163 return glib_->KeyFileHasGroup(key_file_, group.c_str());
164}
165
166bool KeyFileStore::DeleteKey(const string &group, const string &key) {
167 CHECK(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -0700168 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700169 glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700170 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
Darin Petkov083047b2011-06-23 20:42:48 -0700171 LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
172 << glib_->ConvertErrorToMessage(error);
173 return false;
174 }
175 return true;
176}
177
178bool KeyFileStore::DeleteGroup(const string &group) {
179 CHECK(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -0700180 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700181 glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700182 if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
Darin Petkov86964e02011-06-29 13:49:28 -0700183 LOG(ERROR) << "Failed to delete group " << group << ": "
Darin Petkov083047b2011-06-23 20:42:48 -0700184 << glib_->ConvertErrorToMessage(error);
185 return false;
186 }
187 return true;
188}
189
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700190bool KeyFileStore::SetHeader(const string &header) {
Ben Chancc225ef2014-09-30 13:26:51 -0700191 GError *error = nullptr;
192 glib_->KeyFileSetComment(key_file_, nullptr, nullptr, header.c_str(), &error);
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700193 if (error) {
194 LOG(ERROR) << "Failed to to set header: "
195 << glib_->ConvertErrorToMessage(error);
196 return false;
197 }
198 return true;
199}
200
Darin Petkov083047b2011-06-23 20:42:48 -0700201bool KeyFileStore::GetString(const string &group,
202 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800203 string *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700204 CHECK(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -0700205 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700206 gchar *data =
207 glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error);
208 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200209 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700210 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
211 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700212 return false;
213 }
214 if (value) {
215 *value = data;
216 }
217 glib_->Free(data);
218 return true;
219}
220
221bool KeyFileStore::SetString(const string &group,
222 const string &key,
223 const string &value) {
224 CHECK(key_file_);
225 glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
226 return true;
227}
228
229bool KeyFileStore::GetBool(const string &group,
230 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800231 bool *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700232 CHECK(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -0700233 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700234 gboolean data =
235 glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
236 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200237 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700238 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
239 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700240 return false;
241 }
242 if (value) {
243 *value = data;
244 }
245 return true;
246}
247
248bool KeyFileStore::SetBool(const string &group, const string &key, bool value) {
249 CHECK(key_file_);
250 glib_->KeyFileSetBoolean(key_file_,
251 group.c_str(),
252 key.c_str(),
253 value ? TRUE : FALSE);
254 return true;
255}
256
Paul Stewart0756db92012-01-27 08:34:47 -0800257bool KeyFileStore::GetInt(
258 const string &group, const string &key, int *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700259 CHECK(key_file_);
Ben Chancc225ef2014-09-30 13:26:51 -0700260 GError *error = nullptr;
Darin Petkov083047b2011-06-23 20:42:48 -0700261 gint data =
262 glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error);
263 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200264 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700265 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
266 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700267 return false;
268 }
269 if (value) {
270 *value = data;
271 }
272 return true;
273}
274
275bool KeyFileStore::SetInt(const string &group, const string &key, int value) {
276 CHECK(key_file_);
277 glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
278 return true;
279}
280
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700281bool KeyFileStore::GetUint64(
Ben Chan7fab8972014-08-10 17:14:46 -0700282 const string &group, const string &key, uint64_t *value) const {
283 // Read the value in as a string and then convert to uint64_t because glib's
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700284 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
285 // in unit tests.
286 string data_string;
287 if (!GetString(group, key, &data_string)) {
288 return false;
289 }
290
Ben Chan7fab8972014-08-10 17:14:46 -0700291 uint64_t data;
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700292 if (!base::StringToUint64(data_string, &data)) {
293 SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): "
Ben Chan7fab8972014-08-10 17:14:46 -0700294 << "string to uint64_t conversion failed";
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700295 return false;
296 }
297
298 if (value) {
299 *value = data;
300 }
301
302 return true;
303}
304
305bool KeyFileStore::SetUint64(
Ben Chan7fab8972014-08-10 17:14:46 -0700306 const string &group, const string &key, uint64_t value) {
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700307 // Convert the value to a string first, then save the value because glib's
308 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
309 // unit tests.
310 return SetString(group, key, base::Uint64ToString(value));
311}
312
Darin Petkovb2841fd2011-06-30 12:54:12 -0700313bool KeyFileStore::GetStringList(const string &group,
314 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800315 vector<string> *value) const {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700316 CHECK(key_file_);
317 gsize length = 0;
Ben Chancc225ef2014-09-30 13:26:51 -0700318 GError *error = nullptr;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700319 gchar **data = glib_->KeyFileGetStringList(key_file_,
320 group.c_str(),
321 key.c_str(),
322 &length,
323 &error);
324 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200325 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700326 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
327 << s;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700328 return false;
329 }
330 if (value) {
331 value->assign(data, data + length);
332 }
333 glib_->Strfreev(data);
334 return true;
335}
336
337bool KeyFileStore::SetStringList(const string &group,
338 const string &key,
339 const vector<string> &value) {
340 CHECK(key_file_);
341 vector<const char *> list;
Paul Stewart6db7b242014-05-02 15:34:21 -0700342 for (const auto &string_entry : value) {
343 list.push_back(string_entry.c_str());
Darin Petkovb2841fd2011-06-30 12:54:12 -0700344 }
345 glib_->KeyFileSetStringList(key_file_,
346 group.c_str(),
347 key.c_str(),
348 list.data(),
349 list.size());
350 return true;
351}
352
Darin Petkov86964e02011-06-29 13:49:28 -0700353bool KeyFileStore::GetCryptedString(const string &group,
354 const string &key,
355 string *value) {
356 if (!GetString(group, key, value)) {
357 return false;
358 }
359 if (value) {
360 *value = crypto_.Decrypt(*value);
361 }
362 return true;
363}
364
365bool KeyFileStore::SetCryptedString(const string &group,
366 const string &key,
367 const string &value) {
368 return SetString(group, key, crypto_.Encrypt(value));
369}
370
Paul Stewart5b9ec982013-01-18 14:12:14 -0800371bool KeyFileStore::DoesGroupMatchProperties(
372 const string &group, const KeyValueStore &properties) const {
373 map<string, bool>::const_iterator bool_it;
Paul Stewart6db7b242014-05-02 15:34:21 -0700374 for (const auto &property : properties.bool_properties()) {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800375 bool value;
Paul Stewart6db7b242014-05-02 15:34:21 -0700376 if (!GetBool(group, property.first, &value) || value != property.second) {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800377 return false;
378 }
379 }
Paul Stewart6db7b242014-05-02 15:34:21 -0700380 for (const auto &property : properties.int_properties()) {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800381 int value;
Paul Stewart6db7b242014-05-02 15:34:21 -0700382 if (!GetInt(group, property.first, &value) || value != property.second) {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800383 return false;
384 }
385 }
Paul Stewart6db7b242014-05-02 15:34:21 -0700386 for (const auto &property : properties.string_properties()) {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800387 string value;
Paul Stewart6db7b242014-05-02 15:34:21 -0700388 if (!GetString(group, property.first, &value) || value != property.second) {
Paul Stewart5b9ec982013-01-18 14:12:14 -0800389 return false;
390 }
391 }
392 return true;
393}
394
Darin Petkov083047b2011-06-23 20:42:48 -0700395} // namespace shill