blob: 5117b66b962a656c30526691502d352f449892ef [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
7#include <base/file_util.h>
Paul Stewartdab3b5a2012-07-11 18:25:10 -07008#include <base/string_number_conversions.h>
Gary Morain96970242012-04-20 10:59:58 -07009#include <fcntl.h>
10#include <sys/stat.h>
11#include <sys/types.h>
Darin Petkov083047b2011-06-23 20:42:48 -070012
Christopher Wileyb691efd2012-08-09 13:51:51 -070013#include "shill/logging.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070014
Darin Petkov083047b2011-06-23 20:42:48 -070015using std::set;
16using std::string;
Darin Petkovb2841fd2011-06-30 12:54:12 -070017using std::vector;
Darin Petkov083047b2011-06-23 20:42:48 -070018
19namespace shill {
20
Paul Stewart2ebc16d2012-08-23 10:38:39 -070021const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
22
Darin Petkov86964e02011-06-29 13:49:28 -070023KeyFileStore::KeyFileStore(GLib *glib)
24 : glib_(glib),
25 crypto_(glib),
26 key_file_(NULL) {}
Darin Petkov083047b2011-06-23 20:42:48 -070027
28KeyFileStore::~KeyFileStore() {
29 ReleaseKeyFile();
30}
31
32void KeyFileStore::ReleaseKeyFile() {
33 if (key_file_) {
34 glib_->KeyFileFree(key_file_);
35 key_file_ = NULL;
36 }
37}
38
Paul Stewart0756db92012-01-27 08:34:47 -080039bool KeyFileStore::IsNonEmpty() const {
Paul Stewart5dc40aa2011-10-28 19:43:43 -070040 int64 file_size = 0;
41 return file_util::GetFileSize(path_, &file_size) && file_size != 0;
42}
43
Darin Petkov083047b2011-06-23 20:42:48 -070044bool KeyFileStore::Open() {
45 CHECK(!path_.empty());
46 CHECK(!key_file_);
Darin Petkov86964e02011-06-29 13:49:28 -070047 crypto_.Init();
Darin Petkov083047b2011-06-23 20:42:48 -070048 key_file_ = glib_->KeyFileNew();
Paul Stewart5dc40aa2011-10-28 19:43:43 -070049 if (!IsNonEmpty()) {
Darin Petkov083047b2011-06-23 20:42:48 -070050 LOG(INFO) << "Creating a new key file at " << path_.value();
51 return true;
52 }
53 GError *error = NULL;
54 if (glib_->KeyFileLoadFromFile(
55 key_file_,
56 path_.value().c_str(),
57 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
58 G_KEY_FILE_KEEP_TRANSLATIONS),
59 &error)) {
60 return true;
61 }
62 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
63 << glib_->ConvertErrorToMessage(error);
64 ReleaseKeyFile();
65 return false;
66}
67
68bool KeyFileStore::Close() {
Chris Masoneb9c00592011-10-06 13:10:39 -070069 bool success = Flush();
70 ReleaseKeyFile();
71 return success;
72}
73
74bool KeyFileStore::Flush() {
Darin Petkov083047b2011-06-23 20:42:48 -070075 CHECK(key_file_);
76 GError *error = NULL;
77 gsize length = 0;
78 gchar *data = glib_->KeyFileToData(key_file_, &length, &error);
Darin Petkov083047b2011-06-23 20:42:48 -070079
80 bool success = true;
81 if (path_.empty()) {
82 LOG(ERROR) << "Empty key file path.";
83 success = false;
84 }
85 if (success && (!data || error)) {
86 LOG(ERROR) << "Failed to convert key file to string: "
87 << glib_->ConvertErrorToMessage(error);
88 success = false;
89 }
mukesh agrawalf60e4062011-05-27 13:13:41 -070090 if (success) {
Gary Morain96970242012-04-20 10:59:58 -070091 // The profile file must be accessible by the owner only.
92 int fd = creat(path_.value().c_str(), S_IRUSR | S_IWUSR);
93 if (fd < 0) {
94 LOG(ERROR) << "Failed to create key file " << path_.value();
mukesh agrawalf60e4062011-05-27 13:13:41 -070095 success = false;
Gary Morain96970242012-04-20 10:59:58 -070096 } else {
97 int written = file_util::WriteFileDescriptor(fd, data, length);
98 if (written < 0 || (static_cast<gsize>(written) != length)) {
99 LOG(ERROR) << "Failed to store key file: " << path_.value();
100 success = false;
101 }
102 close(fd);
mukesh agrawalf60e4062011-05-27 13:13:41 -0700103 }
Darin Petkov083047b2011-06-23 20:42:48 -0700104 }
105 glib_->Free(data);
106 return success;
107}
108
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700109bool KeyFileStore::MarkAsCorrupted() {
110 LOG(INFO) << "In " << __func__ << " for " << path_.value();
111 if (path_.empty()) {
112 LOG(ERROR) << "Empty key file path.";
113 return false;
114 }
115 string corrupted_path = path_.value() + kCorruptSuffix;
116 int ret = rename(path_.value().c_str(), corrupted_path.c_str());
117 if (ret != 0) {
118 PLOG(ERROR) << "File rename failed";
119 return false;
120 }
121 return true;
122}
123
Paul Stewart0756db92012-01-27 08:34:47 -0800124set<string> KeyFileStore::GetGroups() const {
Darin Petkov083047b2011-06-23 20:42:48 -0700125 CHECK(key_file_);
126 gsize length = 0;
Paul Stewarta41e38d2011-11-11 07:47:29 -0800127 gchar **groups = glib_->KeyFileGetGroups(key_file_, &length);
Darin Petkov083047b2011-06-23 20:42:48 -0700128 if (!groups) {
129 LOG(ERROR) << "Unable to obtain groups.";
130 return set<string>();
131 }
132 set<string> group_set(groups, groups + length);
133 glib_->Strfreev(groups);
134 return group_set;
135}
136
Paul Stewarta41e38d2011-11-11 07:47:29 -0800137// Returns a set so that caller can easily test whether a particular group
138// is contained within this collection.
Paul Stewart0756db92012-01-27 08:34:47 -0800139set<string> KeyFileStore::GetGroupsWithKey(const string &key) const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800140 set<string> groups = GetGroups();
141 set<string> groups_with_key;
142 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
143 if (glib_->KeyFileHasKey(key_file_, (*it).c_str(), key.c_str(), NULL)) {
144 groups_with_key.insert(*it);
145 }
146 }
147 return groups_with_key;
148}
149
Paul Stewart0756db92012-01-27 08:34:47 -0800150bool KeyFileStore::ContainsGroup(const string &group) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700151 CHECK(key_file_);
152 return glib_->KeyFileHasGroup(key_file_, group.c_str());
153}
154
155bool KeyFileStore::DeleteKey(const string &group, const string &key) {
156 CHECK(key_file_);
157 GError *error = NULL;
158 glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700159 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
Darin Petkov083047b2011-06-23 20:42:48 -0700160 LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
161 << glib_->ConvertErrorToMessage(error);
162 return false;
163 }
164 return true;
165}
166
167bool KeyFileStore::DeleteGroup(const string &group) {
168 CHECK(key_file_);
169 GError *error = NULL;
170 glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700171 if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
Darin Petkov86964e02011-06-29 13:49:28 -0700172 LOG(ERROR) << "Failed to delete group " << group << ": "
Darin Petkov083047b2011-06-23 20:42:48 -0700173 << glib_->ConvertErrorToMessage(error);
174 return false;
175 }
176 return true;
177}
178
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700179bool KeyFileStore::SetHeader(const string &header) {
180 GError *error = NULL;
181 glib_->KeyFileSetComment(key_file_, NULL, NULL, header.c_str(), &error);
182 if (error) {
183 LOG(ERROR) << "Failed to to set header: "
184 << glib_->ConvertErrorToMessage(error);
185 return false;
186 }
187 return true;
188}
189
Darin Petkov083047b2011-06-23 20:42:48 -0700190bool KeyFileStore::GetString(const string &group,
191 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800192 string *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700193 CHECK(key_file_);
194 GError *error = NULL;
195 gchar *data =
196 glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error);
197 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200198 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700199 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
200 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700201 return false;
202 }
203 if (value) {
204 *value = data;
205 }
206 glib_->Free(data);
207 return true;
208}
209
210bool KeyFileStore::SetString(const string &group,
211 const string &key,
212 const string &value) {
213 CHECK(key_file_);
214 glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
215 return true;
216}
217
218bool KeyFileStore::GetBool(const string &group,
219 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800220 bool *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700221 CHECK(key_file_);
222 GError *error = NULL;
223 gboolean data =
224 glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
225 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200226 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700227 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
228 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700229 return false;
230 }
231 if (value) {
232 *value = data;
233 }
234 return true;
235}
236
237bool KeyFileStore::SetBool(const string &group, const string &key, bool value) {
238 CHECK(key_file_);
239 glib_->KeyFileSetBoolean(key_file_,
240 group.c_str(),
241 key.c_str(),
242 value ? TRUE : FALSE);
243 return true;
244}
245
Paul Stewart0756db92012-01-27 08:34:47 -0800246bool KeyFileStore::GetInt(
247 const string &group, const string &key, int *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700248 CHECK(key_file_);
249 GError *error = NULL;
250 gint data =
251 glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error);
252 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200253 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700254 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
255 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700256 return false;
257 }
258 if (value) {
259 *value = data;
260 }
261 return true;
262}
263
264bool KeyFileStore::SetInt(const string &group, const string &key, int value) {
265 CHECK(key_file_);
266 glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
267 return true;
268}
269
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700270bool KeyFileStore::GetUint64(
271 const string &group, const string &key, uint64 *value) const {
272 // Read the value in as a string and then convert to uint64 because glib's
273 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
274 // in unit tests.
275 string data_string;
276 if (!GetString(group, key, &data_string)) {
277 return false;
278 }
279
280 uint64 data;
281 if (!base::StringToUint64(data_string, &data)) {
282 SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): "
283 << "string to uint64 conversion failed";
284 return false;
285 }
286
287 if (value) {
288 *value = data;
289 }
290
291 return true;
292}
293
294bool KeyFileStore::SetUint64(
295 const string &group, const string &key, uint64 value) {
296 // Convert the value to a string first, then save the value because glib's
297 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
298 // unit tests.
299 return SetString(group, key, base::Uint64ToString(value));
300}
301
Darin Petkovb2841fd2011-06-30 12:54:12 -0700302bool KeyFileStore::GetStringList(const string &group,
303 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800304 vector<string> *value) const {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700305 CHECK(key_file_);
306 gsize length = 0;
307 GError *error = NULL;
308 gchar **data = glib_->KeyFileGetStringList(key_file_,
309 group.c_str(),
310 key.c_str(),
311 &length,
312 &error);
313 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200314 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700315 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
316 << s;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700317 return false;
318 }
319 if (value) {
320 value->assign(data, data + length);
321 }
322 glib_->Strfreev(data);
323 return true;
324}
325
326bool KeyFileStore::SetStringList(const string &group,
327 const string &key,
328 const vector<string> &value) {
329 CHECK(key_file_);
330 vector<const char *> list;
331 for (vector<string>::const_iterator it = value.begin();
332 it != value.end(); ++it) {
333 list.push_back(it->c_str());
334 }
335 glib_->KeyFileSetStringList(key_file_,
336 group.c_str(),
337 key.c_str(),
338 list.data(),
339 list.size());
340 return true;
341}
342
Darin Petkov86964e02011-06-29 13:49:28 -0700343bool KeyFileStore::GetCryptedString(const string &group,
344 const string &key,
345 string *value) {
346 if (!GetString(group, key, value)) {
347 return false;
348 }
349 if (value) {
350 *value = crypto_.Decrypt(*value);
351 }
352 return true;
353}
354
355bool KeyFileStore::SetCryptedString(const string &group,
356 const string &key,
357 const string &value) {
358 return SetString(group, key, crypto_.Encrypt(value));
359}
360
Darin Petkov083047b2011-06-23 20:42:48 -0700361} // namespace shill