blob: 3127f4048757553e91289168790983e3f5b4cf92 [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
Darin Petkov083047b2011-06-23 20:42:48 -07009#include <base/file_util.h>
Paul Stewartdab3b5a2012-07-11 18:25:10 -070010#include <base/string_number_conversions.h>
Gary Morain96970242012-04-20 10:59:58 -070011#include <fcntl.h>
12#include <sys/stat.h>
13#include <sys/types.h>
Paul Stewart79963642013-06-12 15:44:17 -070014#include <unistd.h>
Darin Petkov083047b2011-06-23 20:42:48 -070015
Paul Stewart5b9ec982013-01-18 14:12:14 -080016#include "shill/key_value_store.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070017#include "shill/logging.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070018
Paul Stewart5b9ec982013-01-18 14:12:14 -080019using std::map;
Darin Petkov083047b2011-06-23 20:42:48 -070020using std::set;
21using std::string;
Darin Petkovb2841fd2011-06-30 12:54:12 -070022using std::vector;
Darin Petkov083047b2011-06-23 20:42:48 -070023
24namespace shill {
25
Paul Stewart2ebc16d2012-08-23 10:38:39 -070026const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
27
Darin Petkov86964e02011-06-29 13:49:28 -070028KeyFileStore::KeyFileStore(GLib *glib)
29 : glib_(glib),
30 crypto_(glib),
31 key_file_(NULL) {}
Darin Petkov083047b2011-06-23 20:42:48 -070032
33KeyFileStore::~KeyFileStore() {
34 ReleaseKeyFile();
35}
36
37void KeyFileStore::ReleaseKeyFile() {
38 if (key_file_) {
39 glib_->KeyFileFree(key_file_);
40 key_file_ = NULL;
41 }
42}
43
Paul Stewart0756db92012-01-27 08:34:47 -080044bool KeyFileStore::IsNonEmpty() const {
Paul Stewart5dc40aa2011-10-28 19:43:43 -070045 int64 file_size = 0;
46 return file_util::GetFileSize(path_, &file_size) && file_size != 0;
47}
48
Darin Petkov083047b2011-06-23 20:42:48 -070049bool KeyFileStore::Open() {
50 CHECK(!path_.empty());
51 CHECK(!key_file_);
Darin Petkov86964e02011-06-29 13:49:28 -070052 crypto_.Init();
Darin Petkov083047b2011-06-23 20:42:48 -070053 key_file_ = glib_->KeyFileNew();
Paul Stewart5dc40aa2011-10-28 19:43:43 -070054 if (!IsNonEmpty()) {
Darin Petkov083047b2011-06-23 20:42:48 -070055 LOG(INFO) << "Creating a new key file at " << path_.value();
56 return true;
57 }
58 GError *error = NULL;
59 if (glib_->KeyFileLoadFromFile(
60 key_file_,
61 path_.value().c_str(),
62 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
63 G_KEY_FILE_KEEP_TRANSLATIONS),
64 &error)) {
65 return true;
66 }
67 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
68 << glib_->ConvertErrorToMessage(error);
69 ReleaseKeyFile();
70 return false;
71}
72
73bool KeyFileStore::Close() {
Chris Masoneb9c00592011-10-06 13:10:39 -070074 bool success = Flush();
75 ReleaseKeyFile();
76 return success;
77}
78
79bool KeyFileStore::Flush() {
Darin Petkov083047b2011-06-23 20:42:48 -070080 CHECK(key_file_);
81 GError *error = NULL;
82 gsize length = 0;
83 gchar *data = glib_->KeyFileToData(key_file_, &length, &error);
Darin Petkov083047b2011-06-23 20:42:48 -070084
85 bool success = true;
86 if (path_.empty()) {
87 LOG(ERROR) << "Empty key file path.";
88 success = false;
89 }
90 if (success && (!data || error)) {
91 LOG(ERROR) << "Failed to convert key file to string: "
92 << glib_->ConvertErrorToMessage(error);
93 success = false;
94 }
mukesh agrawalf60e4062011-05-27 13:13:41 -070095 if (success) {
Gary Morain96970242012-04-20 10:59:58 -070096 // The profile file must be accessible by the owner only.
97 int fd = creat(path_.value().c_str(), S_IRUSR | S_IWUSR);
98 if (fd < 0) {
99 LOG(ERROR) << "Failed to create key file " << path_.value();
mukesh agrawalf60e4062011-05-27 13:13:41 -0700100 success = false;
Gary Morain96970242012-04-20 10:59:58 -0700101 } else {
102 int written = file_util::WriteFileDescriptor(fd, data, length);
103 if (written < 0 || (static_cast<gsize>(written) != length)) {
104 LOG(ERROR) << "Failed to store key file: " << path_.value();
105 success = false;
106 }
Paul Stewart79963642013-06-12 15:44:17 -0700107 // Call fsync() on this fd so that this file is completely written out,
108 // since it is pivotal to our connectivity that these files remain
109 // intact.
110 fsync(fd);
Gary Morain96970242012-04-20 10:59:58 -0700111 close(fd);
mukesh agrawalf60e4062011-05-27 13:13:41 -0700112 }
Darin Petkov083047b2011-06-23 20:42:48 -0700113 }
114 glib_->Free(data);
115 return success;
116}
117
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700118bool KeyFileStore::MarkAsCorrupted() {
119 LOG(INFO) << "In " << __func__ << " for " << path_.value();
120 if (path_.empty()) {
121 LOG(ERROR) << "Empty key file path.";
122 return false;
123 }
124 string corrupted_path = path_.value() + kCorruptSuffix;
125 int ret = rename(path_.value().c_str(), corrupted_path.c_str());
126 if (ret != 0) {
127 PLOG(ERROR) << "File rename failed";
128 return false;
129 }
130 return true;
131}
132
Paul Stewart0756db92012-01-27 08:34:47 -0800133set<string> KeyFileStore::GetGroups() const {
Darin Petkov083047b2011-06-23 20:42:48 -0700134 CHECK(key_file_);
135 gsize length = 0;
Paul Stewarta41e38d2011-11-11 07:47:29 -0800136 gchar **groups = glib_->KeyFileGetGroups(key_file_, &length);
Darin Petkov083047b2011-06-23 20:42:48 -0700137 if (!groups) {
138 LOG(ERROR) << "Unable to obtain groups.";
139 return set<string>();
140 }
141 set<string> group_set(groups, groups + length);
142 glib_->Strfreev(groups);
143 return group_set;
144}
145
Paul Stewarta41e38d2011-11-11 07:47:29 -0800146// Returns a set so that caller can easily test whether a particular group
147// is contained within this collection.
Paul Stewart0756db92012-01-27 08:34:47 -0800148set<string> KeyFileStore::GetGroupsWithKey(const string &key) const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800149 set<string> groups = GetGroups();
150 set<string> groups_with_key;
151 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
152 if (glib_->KeyFileHasKey(key_file_, (*it).c_str(), key.c_str(), NULL)) {
153 groups_with_key.insert(*it);
154 }
155 }
156 return groups_with_key;
157}
158
Paul Stewart5b9ec982013-01-18 14:12:14 -0800159set<string> KeyFileStore::GetGroupsWithProperties(
160 const KeyValueStore &properties) const {
161 set<string> groups = GetGroups();
162 set<string> groups_with_properties;
163 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
164 if (DoesGroupMatchProperties(*it, properties)) {
165 groups_with_properties.insert(*it);
166 }
167 }
168 return groups_with_properties;
169}
170
Paul Stewart0756db92012-01-27 08:34:47 -0800171bool KeyFileStore::ContainsGroup(const string &group) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700172 CHECK(key_file_);
173 return glib_->KeyFileHasGroup(key_file_, group.c_str());
174}
175
176bool KeyFileStore::DeleteKey(const string &group, const string &key) {
177 CHECK(key_file_);
178 GError *error = NULL;
179 glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700180 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
Darin Petkov083047b2011-06-23 20:42:48 -0700181 LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
182 << glib_->ConvertErrorToMessage(error);
183 return false;
184 }
185 return true;
186}
187
188bool KeyFileStore::DeleteGroup(const string &group) {
189 CHECK(key_file_);
190 GError *error = NULL;
191 glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700192 if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
Darin Petkov86964e02011-06-29 13:49:28 -0700193 LOG(ERROR) << "Failed to delete group " << group << ": "
Darin Petkov083047b2011-06-23 20:42:48 -0700194 << glib_->ConvertErrorToMessage(error);
195 return false;
196 }
197 return true;
198}
199
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700200bool KeyFileStore::SetHeader(const string &header) {
201 GError *error = NULL;
202 glib_->KeyFileSetComment(key_file_, NULL, NULL, header.c_str(), &error);
203 if (error) {
204 LOG(ERROR) << "Failed to to set header: "
205 << glib_->ConvertErrorToMessage(error);
206 return false;
207 }
208 return true;
209}
210
Darin Petkov083047b2011-06-23 20:42:48 -0700211bool KeyFileStore::GetString(const string &group,
212 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800213 string *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700214 CHECK(key_file_);
215 GError *error = NULL;
216 gchar *data =
217 glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error);
218 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200219 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700220 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
221 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700222 return false;
223 }
224 if (value) {
225 *value = data;
226 }
227 glib_->Free(data);
228 return true;
229}
230
231bool KeyFileStore::SetString(const string &group,
232 const string &key,
233 const string &value) {
234 CHECK(key_file_);
235 glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
236 return true;
237}
238
239bool KeyFileStore::GetBool(const string &group,
240 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800241 bool *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700242 CHECK(key_file_);
243 GError *error = NULL;
244 gboolean data =
245 glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
246 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200247 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700248 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
249 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700250 return false;
251 }
252 if (value) {
253 *value = data;
254 }
255 return true;
256}
257
258bool KeyFileStore::SetBool(const string &group, const string &key, bool value) {
259 CHECK(key_file_);
260 glib_->KeyFileSetBoolean(key_file_,
261 group.c_str(),
262 key.c_str(),
263 value ? TRUE : FALSE);
264 return true;
265}
266
Paul Stewart0756db92012-01-27 08:34:47 -0800267bool KeyFileStore::GetInt(
268 const string &group, const string &key, int *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700269 CHECK(key_file_);
270 GError *error = NULL;
271 gint data =
272 glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error);
273 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200274 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700275 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
276 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700277 return false;
278 }
279 if (value) {
280 *value = data;
281 }
282 return true;
283}
284
285bool KeyFileStore::SetInt(const string &group, const string &key, int value) {
286 CHECK(key_file_);
287 glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
288 return true;
289}
290
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700291bool KeyFileStore::GetUint64(
292 const string &group, const string &key, uint64 *value) const {
293 // Read the value in as a string and then convert to uint64 because glib's
294 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
295 // in unit tests.
296 string data_string;
297 if (!GetString(group, key, &data_string)) {
298 return false;
299 }
300
301 uint64 data;
302 if (!base::StringToUint64(data_string, &data)) {
303 SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): "
304 << "string to uint64 conversion failed";
305 return false;
306 }
307
308 if (value) {
309 *value = data;
310 }
311
312 return true;
313}
314
315bool KeyFileStore::SetUint64(
316 const string &group, const string &key, uint64 value) {
317 // Convert the value to a string first, then save the value because glib's
318 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
319 // unit tests.
320 return SetString(group, key, base::Uint64ToString(value));
321}
322
Darin Petkovb2841fd2011-06-30 12:54:12 -0700323bool KeyFileStore::GetStringList(const string &group,
324 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800325 vector<string> *value) const {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700326 CHECK(key_file_);
327 gsize length = 0;
328 GError *error = NULL;
329 gchar **data = glib_->KeyFileGetStringList(key_file_,
330 group.c_str(),
331 key.c_str(),
332 &length,
333 &error);
334 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200335 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700336 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
337 << s;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700338 return false;
339 }
340 if (value) {
341 value->assign(data, data + length);
342 }
343 glib_->Strfreev(data);
344 return true;
345}
346
347bool KeyFileStore::SetStringList(const string &group,
348 const string &key,
349 const vector<string> &value) {
350 CHECK(key_file_);
351 vector<const char *> list;
352 for (vector<string>::const_iterator it = value.begin();
353 it != value.end(); ++it) {
354 list.push_back(it->c_str());
355 }
356 glib_->KeyFileSetStringList(key_file_,
357 group.c_str(),
358 key.c_str(),
359 list.data(),
360 list.size());
361 return true;
362}
363
Darin Petkov86964e02011-06-29 13:49:28 -0700364bool KeyFileStore::GetCryptedString(const string &group,
365 const string &key,
366 string *value) {
367 if (!GetString(group, key, value)) {
368 return false;
369 }
370 if (value) {
371 *value = crypto_.Decrypt(*value);
372 }
373 return true;
374}
375
376bool KeyFileStore::SetCryptedString(const string &group,
377 const string &key,
378 const string &value) {
379 return SetString(group, key, crypto_.Encrypt(value));
380}
381
Paul Stewart5b9ec982013-01-18 14:12:14 -0800382bool KeyFileStore::DoesGroupMatchProperties(
383 const string &group, const KeyValueStore &properties) const {
384 map<string, bool>::const_iterator bool_it;
385 for (bool_it = properties.bool_properties().begin();
386 bool_it != properties.bool_properties().end();
387 ++bool_it) {
388 bool value;
389 if (!GetBool(group, bool_it->first, &value) ||
390 value != bool_it->second) {
391 return false;
392 }
393 }
394 map<string, int32>::const_iterator int_it;
395 for (int_it = properties.int_properties().begin();
396 int_it != properties.int_properties().end();
397 ++int_it) {
398 int value;
399 if (!GetInt(group, int_it->first, &value) ||
400 value != int_it->second) {
401 return false;
402 }
403 }
404 map<string, string>::const_iterator string_it;
405 for (string_it = properties.string_properties().begin();
406 string_it != properties.string_properties().end();
407 ++string_it) {
408 string value;
409 if (!GetString(group, string_it->first, &value) ||
410 value != string_it->second) {
411 return false;
412 }
413 }
414 return true;
415}
416
Darin Petkov083047b2011-06-23 20:42:48 -0700417} // namespace shill