blob: dd049254d571f67858a29a86ec653243e678e20f [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>
8#include <base/logging.h>
Paul Stewartdab3b5a2012-07-11 18:25:10 -07009#include <base/string_number_conversions.h>
Gary Morain96970242012-04-20 10:59:58 -070010#include <fcntl.h>
11#include <sys/stat.h>
12#include <sys/types.h>
Darin Petkov083047b2011-06-23 20:42:48 -070013
Ben Chanfad4a0b2012-04-18 15:49:59 -070014#include "shill/scope_logger.h"
15
Darin Petkov083047b2011-06-23 20:42:48 -070016using std::set;
17using std::string;
Darin Petkovb2841fd2011-06-30 12:54:12 -070018using std::vector;
Darin Petkov083047b2011-06-23 20:42:48 -070019
20namespace shill {
21
Darin Petkov86964e02011-06-29 13:49:28 -070022KeyFileStore::KeyFileStore(GLib *glib)
23 : glib_(glib),
24 crypto_(glib),
25 key_file_(NULL) {}
Darin Petkov083047b2011-06-23 20:42:48 -070026
27KeyFileStore::~KeyFileStore() {
28 ReleaseKeyFile();
29}
30
31void KeyFileStore::ReleaseKeyFile() {
32 if (key_file_) {
33 glib_->KeyFileFree(key_file_);
34 key_file_ = NULL;
35 }
36}
37
Paul Stewart0756db92012-01-27 08:34:47 -080038bool KeyFileStore::IsNonEmpty() const {
Paul Stewart5dc40aa2011-10-28 19:43:43 -070039 int64 file_size = 0;
40 return file_util::GetFileSize(path_, &file_size) && file_size != 0;
41}
42
Darin Petkov083047b2011-06-23 20:42:48 -070043bool KeyFileStore::Open() {
44 CHECK(!path_.empty());
45 CHECK(!key_file_);
Darin Petkov86964e02011-06-29 13:49:28 -070046 crypto_.Init();
Darin Petkov083047b2011-06-23 20:42:48 -070047 key_file_ = glib_->KeyFileNew();
Paul Stewart5dc40aa2011-10-28 19:43:43 -070048 if (!IsNonEmpty()) {
Darin Petkov083047b2011-06-23 20:42:48 -070049 LOG(INFO) << "Creating a new key file at " << path_.value();
50 return true;
51 }
52 GError *error = NULL;
53 if (glib_->KeyFileLoadFromFile(
54 key_file_,
55 path_.value().c_str(),
56 static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
57 G_KEY_FILE_KEEP_TRANSLATIONS),
58 &error)) {
59 return true;
60 }
61 LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
62 << glib_->ConvertErrorToMessage(error);
63 ReleaseKeyFile();
64 return false;
65}
66
67bool KeyFileStore::Close() {
Chris Masoneb9c00592011-10-06 13:10:39 -070068 bool success = Flush();
69 ReleaseKeyFile();
70 return success;
71}
72
73bool KeyFileStore::Flush() {
Darin Petkov083047b2011-06-23 20:42:48 -070074 CHECK(key_file_);
75 GError *error = NULL;
76 gsize length = 0;
77 gchar *data = glib_->KeyFileToData(key_file_, &length, &error);
Darin Petkov083047b2011-06-23 20:42:48 -070078
79 bool success = true;
80 if (path_.empty()) {
81 LOG(ERROR) << "Empty key file path.";
82 success = false;
83 }
84 if (success && (!data || error)) {
85 LOG(ERROR) << "Failed to convert key file to string: "
86 << glib_->ConvertErrorToMessage(error);
87 success = false;
88 }
mukesh agrawalf60e4062011-05-27 13:13:41 -070089 if (success) {
Gary Morain96970242012-04-20 10:59:58 -070090 // The profile file must be accessible by the owner only.
91 int fd = creat(path_.value().c_str(), S_IRUSR | S_IWUSR);
92 if (fd < 0) {
93 LOG(ERROR) << "Failed to create key file " << path_.value();
mukesh agrawalf60e4062011-05-27 13:13:41 -070094 success = false;
Gary Morain96970242012-04-20 10:59:58 -070095 } else {
96 int written = file_util::WriteFileDescriptor(fd, data, length);
97 if (written < 0 || (static_cast<gsize>(written) != length)) {
98 LOG(ERROR) << "Failed to store key file: " << path_.value();
99 success = false;
100 }
101 close(fd);
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 Stewart0756db92012-01-27 08:34:47 -0800108set<string> KeyFileStore::GetGroups() const {
Darin Petkov083047b2011-06-23 20:42:48 -0700109 CHECK(key_file_);
110 gsize length = 0;
Paul Stewarta41e38d2011-11-11 07:47:29 -0800111 gchar **groups = glib_->KeyFileGetGroups(key_file_, &length);
Darin Petkov083047b2011-06-23 20:42:48 -0700112 if (!groups) {
113 LOG(ERROR) << "Unable to obtain groups.";
114 return set<string>();
115 }
116 set<string> group_set(groups, groups + length);
117 glib_->Strfreev(groups);
118 return group_set;
119}
120
Paul Stewarta41e38d2011-11-11 07:47:29 -0800121// Returns a set so that caller can easily test whether a particular group
122// is contained within this collection.
Paul Stewart0756db92012-01-27 08:34:47 -0800123set<string> KeyFileStore::GetGroupsWithKey(const string &key) const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800124 set<string> groups = GetGroups();
125 set<string> groups_with_key;
126 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
127 if (glib_->KeyFileHasKey(key_file_, (*it).c_str(), key.c_str(), NULL)) {
128 groups_with_key.insert(*it);
129 }
130 }
131 return groups_with_key;
132}
133
Paul Stewart0756db92012-01-27 08:34:47 -0800134bool KeyFileStore::ContainsGroup(const string &group) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700135 CHECK(key_file_);
136 return glib_->KeyFileHasGroup(key_file_, group.c_str());
137}
138
139bool KeyFileStore::DeleteKey(const string &group, const string &key) {
140 CHECK(key_file_);
141 GError *error = NULL;
142 glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700143 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
Darin Petkov083047b2011-06-23 20:42:48 -0700144 LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
145 << glib_->ConvertErrorToMessage(error);
146 return false;
147 }
148 return true;
149}
150
151bool KeyFileStore::DeleteGroup(const string &group) {
152 CHECK(key_file_);
153 GError *error = NULL;
154 glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error);
Chris Masone9d779932011-08-25 16:33:41 -0700155 if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
Darin Petkov86964e02011-06-29 13:49:28 -0700156 LOG(ERROR) << "Failed to delete group " << group << ": "
Darin Petkov083047b2011-06-23 20:42:48 -0700157 << glib_->ConvertErrorToMessage(error);
158 return false;
159 }
160 return true;
161}
162
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700163bool KeyFileStore::SetHeader(const string &header) {
164 GError *error = NULL;
165 glib_->KeyFileSetComment(key_file_, NULL, NULL, header.c_str(), &error);
166 if (error) {
167 LOG(ERROR) << "Failed to to set header: "
168 << glib_->ConvertErrorToMessage(error);
169 return false;
170 }
171 return true;
172}
173
Darin Petkov083047b2011-06-23 20:42:48 -0700174bool KeyFileStore::GetString(const string &group,
175 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800176 string *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700177 CHECK(key_file_);
178 GError *error = NULL;
179 gchar *data =
180 glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error);
181 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200182 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700183 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
184 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700185 return false;
186 }
187 if (value) {
188 *value = data;
189 }
190 glib_->Free(data);
191 return true;
192}
193
194bool KeyFileStore::SetString(const string &group,
195 const string &key,
196 const string &value) {
197 CHECK(key_file_);
198 glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
199 return true;
200}
201
202bool KeyFileStore::GetBool(const string &group,
203 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800204 bool *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700205 CHECK(key_file_);
206 GError *error = NULL;
207 gboolean data =
208 glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
209 if (error) {
Darin Petkovc529c832012-04-18 14:59:42 +0200210 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700211 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
212 << s;
Darin Petkov083047b2011-06-23 20:42:48 -0700213 return false;
214 }
215 if (value) {
216 *value = data;
217 }
218 return true;
219}
220
221bool KeyFileStore::SetBool(const string &group, const string &key, bool value) {
222 CHECK(key_file_);
223 glib_->KeyFileSetBoolean(key_file_,
224 group.c_str(),
225 key.c_str(),
226 value ? TRUE : FALSE);
227 return true;
228}
229
Paul Stewart0756db92012-01-27 08:34:47 -0800230bool KeyFileStore::GetInt(
231 const string &group, const string &key, int *value) const {
Darin Petkov083047b2011-06-23 20:42:48 -0700232 CHECK(key_file_);
233 GError *error = NULL;
234 gint data =
235 glib_->KeyFileGetInteger(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::SetInt(const string &group, const string &key, int value) {
249 CHECK(key_file_);
250 glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
251 return true;
252}
253
Paul Stewartdab3b5a2012-07-11 18:25:10 -0700254bool KeyFileStore::GetUint64(
255 const string &group, const string &key, uint64 *value) const {
256 // Read the value in as a string and then convert to uint64 because glib's
257 // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
258 // in unit tests.
259 string data_string;
260 if (!GetString(group, key, &data_string)) {
261 return false;
262 }
263
264 uint64 data;
265 if (!base::StringToUint64(data_string, &data)) {
266 SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): "
267 << "string to uint64 conversion failed";
268 return false;
269 }
270
271 if (value) {
272 *value = data;
273 }
274
275 return true;
276}
277
278bool KeyFileStore::SetUint64(
279 const string &group, const string &key, uint64 value) {
280 // Convert the value to a string first, then save the value because glib's
281 // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
282 // unit tests.
283 return SetString(group, key, base::Uint64ToString(value));
284}
285
Darin Petkovb2841fd2011-06-30 12:54:12 -0700286bool KeyFileStore::GetStringList(const string &group,
287 const string &key,
Paul Stewart0756db92012-01-27 08:34:47 -0800288 vector<string> *value) const {
Darin Petkovb2841fd2011-06-30 12:54:12 -0700289 CHECK(key_file_);
290 gsize length = 0;
291 GError *error = NULL;
292 gchar **data = glib_->KeyFileGetStringList(key_file_,
293 group.c_str(),
294 key.c_str(),
295 &length,
296 &error);
297 if (!data) {
Darin Petkovc529c832012-04-18 14:59:42 +0200298 string s = glib_->ConvertErrorToMessage(error);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700299 SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): "
300 << s;
Darin Petkovb2841fd2011-06-30 12:54:12 -0700301 return false;
302 }
303 if (value) {
304 value->assign(data, data + length);
305 }
306 glib_->Strfreev(data);
307 return true;
308}
309
310bool KeyFileStore::SetStringList(const string &group,
311 const string &key,
312 const vector<string> &value) {
313 CHECK(key_file_);
314 vector<const char *> list;
315 for (vector<string>::const_iterator it = value.begin();
316 it != value.end(); ++it) {
317 list.push_back(it->c_str());
318 }
319 glib_->KeyFileSetStringList(key_file_,
320 group.c_str(),
321 key.c_str(),
322 list.data(),
323 list.size());
324 return true;
325}
326
Darin Petkov86964e02011-06-29 13:49:28 -0700327bool KeyFileStore::GetCryptedString(const string &group,
328 const string &key,
329 string *value) {
330 if (!GetString(group, key, value)) {
331 return false;
332 }
333 if (value) {
334 *value = crypto_.Decrypt(*value);
335 }
336 return true;
337}
338
339bool KeyFileStore::SetCryptedString(const string &group,
340 const string &key,
341 const string &value) {
342 return SetString(group, key, crypto_.Encrypt(value));
343}
344
Darin Petkov083047b2011-06-23 20:42:48 -0700345} // namespace shill