blob: 76839f4140f04d1a721ed06977f67a8fb5e85836 [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>
Darin Petkov083047b2011-06-23 20:42:48 -070014
Paul Stewart5b9ec982013-01-18 14:12:14 -080015#include "shill/key_value_store.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070016#include "shill/logging.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070017
Paul Stewart5b9ec982013-01-18 14:12:14 -080018using std::map;
Darin Petkov083047b2011-06-23 20:42:48 -070019using std::set;
20using std::string;
Darin Petkovb2841fd2011-06-30 12:54:12 -070021using std::vector;
Darin Petkov083047b2011-06-23 20:42:48 -070022
23namespace shill {
24
Paul Stewart2ebc16d2012-08-23 10:38:39 -070025const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
26
Darin Petkov86964e02011-06-29 13:49:28 -070027KeyFileStore::KeyFileStore(GLib *glib)
28 : glib_(glib),
29 crypto_(glib),
30 key_file_(NULL) {}
Darin Petkov083047b2011-06-23 20:42:48 -070031
32KeyFileStore::~KeyFileStore() {
33 ReleaseKeyFile();
34}
35
36void KeyFileStore::ReleaseKeyFile() {
37 if (key_file_) {
38 glib_->KeyFileFree(key_file_);
39 key_file_ = NULL;
40 }
41}
42
Paul Stewart0756db92012-01-27 08:34:47 -080043bool KeyFileStore::IsNonEmpty() const {
Paul Stewart5dc40aa2011-10-28 19:43:43 -070044 int64 file_size = 0;
45 return file_util::GetFileSize(path_, &file_size) && file_size != 0;
46}
47
Darin Petkov083047b2011-06-23 20:42:48 -070048bool KeyFileStore::Open() {
49 CHECK(!path_.empty());
50 CHECK(!key_file_);
Darin Petkov86964e02011-06-29 13:49:28 -070051 crypto_.Init();
Darin Petkov083047b2011-06-23 20:42:48 -070052 key_file_ = glib_->KeyFileNew();
Paul Stewart5dc40aa2011-10-28 19:43:43 -070053 if (!IsNonEmpty()) {
Darin Petkov083047b2011-06-23 20:42:48 -070054 LOG(INFO) << "Creating a new key file at " << path_.value();
55 return true;
56 }
57 GError *error = NULL;
58 if (glib_->KeyFileLoadFromFile(
59 key_file_,
60 path_.value().c_str(),
61 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
62 G_KEY_FILE_KEEP_TRANSLATIONS),
63 &error)) {
64 return true;
65 }
66 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
67 << glib_->ConvertErrorToMessage(error);
68 ReleaseKeyFile();
69 return false;
70}
71
72bool KeyFileStore::Close() {
Chris Masoneb9c00592011-10-06 13:10:39 -070073 bool success = Flush();
74 ReleaseKeyFile();
75 return success;
76}
77
78bool KeyFileStore::Flush() {
Darin Petkov083047b2011-06-23 20:42:48 -070079 CHECK(key_file_);
80 GError *error = NULL;
81 gsize length = 0;
82 gchar *data = glib_->KeyFileToData(key_file_, &length, &error);
Darin Petkov083047b2011-06-23 20:42:48 -070083
84 bool success = true;
85 if (path_.empty()) {
86 LOG(ERROR) << "Empty key file path.";
87 success = false;
88 }
89 if (success && (!data || error)) {
90 LOG(ERROR) << "Failed to convert key file to string: "
91 << glib_->ConvertErrorToMessage(error);
92 success = false;
93 }
mukesh agrawalf60e4062011-05-27 13:13:41 -070094 if (success) {
Gary Morain96970242012-04-20 10:59:58 -070095 // The profile file must be accessible by the owner only.
96 int fd = creat(path_.value().c_str(), S_IRUSR | S_IWUSR);
97 if (fd < 0) {
98 LOG(ERROR) << "Failed to create key file " << path_.value();
mukesh agrawalf60e4062011-05-27 13:13:41 -070099 success = false;
Gary Morain96970242012-04-20 10:59:58 -0700100 } else {
101 int written = file_util::WriteFileDescriptor(fd, data, length);
102 if (written < 0 || (static_cast<gsize>(written) != length)) {
103 LOG(ERROR) << "Failed to store key file: " << path_.value();
104 success = false;
105 }
106 close(fd);
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 Stewarta41e38d2011-11-11 07:47:29 -0800131 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 Stewart0756db92012-01-27 08:34:47 -0800143set<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;
146 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
147 if (glib_->KeyFileHasKey(key_file_, (*it).c_str(), key.c_str(), NULL)) {
148 groups_with_key.insert(*it);
149 }
150 }
151 return groups_with_key;
152}
153
Paul Stewart5b9ec982013-01-18 14:12:14 -0800154set<string> KeyFileStore::GetGroupsWithProperties(
155 const KeyValueStore &properties) const {
156 set<string> groups = GetGroups();
157 set<string> groups_with_properties;
158 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
159 if (DoesGroupMatchProperties(*it, properties)) {
160 groups_with_properties.insert(*it);
161 }
162 }
163 return groups_with_properties;
164}
165
Paul Stewart0756db92012-01-27 08:34:47 -0800166bool 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
171bool KeyFileStore::DeleteKey(const string &group, const string &key) {
172 CHECK(key_file_);
173 GError *error = NULL;
174 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
183bool KeyFileStore::DeleteGroup(const string &group) {
184 CHECK(key_file_);
185 GError *error = NULL;
186 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 Stewart5dc40aa2011-10-28 19:43:43 -0700195bool KeyFileStore::SetHeader(const string &header) {
196 GError *error = NULL;
197 glib_->KeyFileSetComment(key_file_, NULL, NULL, header.c_str(), &error);
198 if (error) {
199 LOG(ERROR) << "Failed to to set header: "
200 << glib_->ConvertErrorToMessage(error);
201 return false;
202 }
203 return true;
204}
205
Darin Petkov083047b2011-06-23 20:42:48 -0700206bool KeyFileStore::GetString(const string &group,
207 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800208 string *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700209 CHECK(key_file_);
210 GError *error = NULL;
211 gchar *data =
212 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);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700215 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
216 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700217 return false;
218 }
219 if (value) {
220 *value = data;
221 }
222 glib_->Free(data);
223 return true;
224}
225
226bool KeyFileStore::SetString(const string &group,
227 const string &key,
228 const string &value) {
229 CHECK(key_file_);
230 glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
231 return true;
232}
233
234bool KeyFileStore::GetBool(const string &group,
235 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800236 bool *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700237 CHECK(key_file_);
238 GError *error = NULL;
239 gboolean data =
240 glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
241 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200242 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700243 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
244 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700245 return false;
246 }
247 if (value) {
248 *value = data;
249 }
250 return true;
251}
252
253bool KeyFileStore::SetBool(const string &group, const string &key, bool value) {
254 CHECK(key_file_);
255 glib_->KeyFileSetBoolean(key_file_,
256 group.c_str(),
257 key.c_str(),
258 value ? TRUE : FALSE);
259 return true;
260}
261
Paul Stewart0756db92012-01-27 08:34:47 -0800262bool KeyFileStore::GetInt(
263 const string &group, const string &key, int *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700264 CHECK(key_file_);
265 GError *error = NULL;
266 gint data =
267 glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error);
268 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200269 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700270 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
271 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700272 return false;
273 }
274 if (value) {
275 *value = data;
276 }
277 return true;
278}
279
280bool KeyFileStore::SetInt(const string &group, const string &key, int value) {
281 CHECK(key_file_);
282 glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
283 return true;
284}
285
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700286bool KeyFileStore::GetUint64(
287 const string &group, const string &key, uint64 *value) const {
288 // Read the value in as a string and then convert to uint64 because glib's
289 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
290 // in unit tests.
291 string data_string;
292 if (!GetString(group, key, &data_string)) {
293 return false;
294 }
295
296 uint64 data;
297 if (!base::StringToUint64(data_string, &data)) {
298 SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): "
299 << "string to uint64 conversion failed";
300 return false;
301 }
302
303 if (value) {
304 *value = data;
305 }
306
307 return true;
308}
309
310bool KeyFileStore::SetUint64(
311 const string &group, const string &key, uint64 value) {
312 // Convert the value to a string first, then save the value because glib's
313 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
314 // unit tests.
315 return SetString(group, key, base::Uint64ToString(value));
316}
317
Darin Petkovb2841fd2011-06-30 12:54:12 -0700318bool KeyFileStore::GetStringList(const string &group,
319 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800320 vector<string> *value) const {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700321 CHECK(key_file_);
322 gsize length = 0;
323 GError *error = NULL;
324 gchar **data = glib_->KeyFileGetStringList(key_file_,
325 group.c_str(),
326 key.c_str(),
327 &length,
328 &error);
329 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200330 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700331 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
332 << s;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700333 return false;
334 }
335 if (value) {
336 value->assign(data, data + length);
337 }
338 glib_->Strfreev(data);
339 return true;
340}
341
342bool KeyFileStore::SetStringList(const string &group,
343 const string &key,
344 const vector<string> &value) {
345 CHECK(key_file_);
346 vector<const char *> list;
347 for (vector<string>::const_iterator it = value.begin();
348 it != value.end(); ++it) {
349 list.push_back(it->c_str());
350 }
351 glib_->KeyFileSetStringList(key_file_,
352 group.c_str(),
353 key.c_str(),
354 list.data(),
355 list.size());
356 return true;
357}
358
Darin Petkov86964e02011-06-29 13:49:28 -0700359bool KeyFileStore::GetCryptedString(const string &group,
360 const string &key,
361 string *value) {
362 if (!GetString(group, key, value)) {
363 return false;
364 }
365 if (value) {
366 *value = crypto_.Decrypt(*value);
367 }
368 return true;
369}
370
371bool KeyFileStore::SetCryptedString(const string &group,
372 const string &key,
373 const string &value) {
374 return SetString(group, key, crypto_.Encrypt(value));
375}
376
Paul Stewart5b9ec982013-01-18 14:12:14 -0800377bool KeyFileStore::DoesGroupMatchProperties(
378 const string &group, const KeyValueStore &properties) const {
379 map<string, bool>::const_iterator bool_it;
380 for (bool_it = properties.bool_properties().begin();
381 bool_it != properties.bool_properties().end();
382 ++bool_it) {
383 bool value;
384 if (!GetBool(group, bool_it->first, &value) ||
385 value != bool_it->second) {
386 return false;
387 }
388 }
389 map<string, int32>::const_iterator int_it;
390 for (int_it = properties.int_properties().begin();
391 int_it != properties.int_properties().end();
392 ++int_it) {
393 int value;
394 if (!GetInt(group, int_it->first, &value) ||
395 value != int_it->second) {
396 return false;
397 }
398 }
399 map<string, string>::const_iterator string_it;
400 for (string_it = properties.string_properties().begin();
401 string_it != properties.string_properties().end();
402 ++string_it) {
403 string value;
404 if (!GetString(group, string_it->first, &value) ||
405 value != string_it->second) {
406 return false;
407 }
408 }
409 return true;
410}
411
Darin Petkov083047b2011-06-23 20:42:48 -0700412} // namespace shill