blob: cfcd4872392d980957f19f20f30085a2455f9b72 [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>
Darin Petkov083047b2011-06-23 20:42:48 -070010#include <base/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),
33 key_file_(NULL) {}
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_);
42 key_file_ = NULL;
43 }
44}
45
Paul Stewart0756db92012-01-27 08:34:47 -080046bool KeyFileStore::IsNonEmpty() const {
Paul Stewart5dc40aa2011-10-28 19:43:43 -070047 int64 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 }
60 GError *error = NULL;
61 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_);
83 GError *error = NULL;
84 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;
141 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
142 if (glib_->KeyFileHasKey(key_file_, (*it).c_str(), key.c_str(), NULL)) {
143 groups_with_key.insert(*it);
144 }
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;
153 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
154 if (DoesGroupMatchProperties(*it, properties)) {
155 groups_with_properties.insert(*it);
156 }
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_);
168 GError *error = NULL;
169 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_);
180 GError *error = NULL;
181 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) {
191 GError *error = NULL;
192 glib_->KeyFileSetComment(key_file_, NULL, NULL, header.c_str(), &error);
193 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_);
205 GError *error = NULL;
206 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_);
233 GError *error = NULL;
234 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_);
260 GError *error = NULL;
261 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(
282 const string &group, const string &key, uint64 *value) const {
283 // Read the value in as a string and then convert to uint64 because glib's
284 // 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
291 uint64 data;
292 if (!base::StringToUint64(data_string, &data)) {
293 SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): "
294 << "string to uint64 conversion failed";
295 return false;
296 }
297
298 if (value) {
299 *value = data;
300 }
301
302 return true;
303}
304
305bool KeyFileStore::SetUint64(
306 const string &group, const string &key, uint64 value) {
307 // 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;
318 GError *error = NULL;
319 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;
342 for (vector<string>::const_iterator it = value.begin();
343 it != value.end(); ++it) {
344 list.push_back(it->c_str());
345 }
346 glib_->KeyFileSetStringList(key_file_,
347 group.c_str(),
348 key.c_str(),
349 list.data(),
350 list.size());
351 return true;
352}
353
Darin Petkov86964e02011-06-29 13:49:28 -0700354bool KeyFileStore::GetCryptedString(const string &group,
355 const string &key,
356 string *value) {
357 if (!GetString(group, key, value)) {
358 return false;
359 }
360 if (value) {
361 *value = crypto_.Decrypt(*value);
362 }
363 return true;
364}
365
366bool KeyFileStore::SetCryptedString(const string &group,
367 const string &key,
368 const string &value) {
369 return SetString(group, key, crypto_.Encrypt(value));
370}
371
Paul Stewart5b9ec982013-01-18 14:12:14 -0800372bool KeyFileStore::DoesGroupMatchProperties(
373 const string &group, const KeyValueStore &properties) const {
374 map<string, bool>::const_iterator bool_it;
375 for (bool_it = properties.bool_properties().begin();
376 bool_it != properties.bool_properties().end();
377 ++bool_it) {
378 bool value;
379 if (!GetBool(group, bool_it->first, &value) ||
380 value != bool_it->second) {
381 return false;
382 }
383 }
384 map<string, int32>::const_iterator int_it;
385 for (int_it = properties.int_properties().begin();
386 int_it != properties.int_properties().end();
387 ++int_it) {
388 int value;
389 if (!GetInt(group, int_it->first, &value) ||
390 value != int_it->second) {
391 return false;
392 }
393 }
394 map<string, string>::const_iterator string_it;
395 for (string_it = properties.string_properties().begin();
396 string_it != properties.string_properties().end();
397 ++string_it) {
398 string value;
399 if (!GetString(group, string_it->first, &value) ||
400 value != string_it->second) {
401 return false;
402 }
403 }
404 return true;
405}
406
Darin Petkov083047b2011-06-23 20:42:48 -0700407} // namespace shill