• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "shill/key_file_store.h"
18 
19 #include <map>
20 
21 #include <base/files/important_file_writer.h>
22 #include <base/files/file_util.h>
23 #include <base/strings/string_number_conversions.h>
24 #include <base/strings/stringprintf.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 #include "shill/key_value_store.h"
31 #include "shill/logging.h"
32 #include "shill/scoped_umask.h"
33 
34 using std::map;
35 using std::set;
36 using std::string;
37 using std::vector;
38 
39 namespace shill {
40 
41 namespace Logging {
42 static auto kModuleLogScope = ScopeLogger::kStorage;
ObjectID(const KeyFileStore * k)43 static string ObjectID(const KeyFileStore* k) { return "(key_file_store)"; }
44 }
45 
46 namespace {
ConvertErrorToMessage(GError * error)47 string ConvertErrorToMessage(GError* error) {
48   if (!error) {
49     return "Unknown GLib error.";
50   }
51   string message =
52     base::StringPrintf("GError(%d): %s", error->code, error->message);
53   g_error_free(error);
54   return message;
55 }
56 }  // namespace
57 
58 const char KeyFileStore::kCorruptSuffix[] = ".corrupted";
59 
KeyFileStore(const base::FilePath & path)60 KeyFileStore::KeyFileStore(const base::FilePath& path)
61     : crypto_(),
62       key_file_(nullptr),
63       path_(path) {
64   CHECK(!path_.empty());
65 }
66 
~KeyFileStore()67 KeyFileStore::~KeyFileStore() {
68   ReleaseKeyFile();
69 }
70 
ReleaseKeyFile()71 void KeyFileStore::ReleaseKeyFile() {
72   if (key_file_) {
73     g_key_file_free(key_file_);
74     key_file_ = nullptr;
75   }
76 }
77 
IsNonEmpty() const78 bool KeyFileStore::IsNonEmpty() const {
79   int64_t file_size = 0;
80   return base::GetFileSize(path_, &file_size) && file_size != 0;
81 }
82 
Open()83 bool KeyFileStore::Open() {
84   CHECK(!key_file_);
85   crypto_.Init();
86   key_file_ = g_key_file_new();
87   if (!IsNonEmpty()) {
88     LOG(INFO) << "Creating a new key file at " << path_.value();
89     return true;
90   }
91   GError* error = nullptr;
92   if (g_key_file_load_from_file(
93           key_file_,
94           path_.value().c_str(),
95           static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
96                                      G_KEY_FILE_KEEP_TRANSLATIONS),
97           &error)) {
98     return true;
99   }
100   LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
101              << ConvertErrorToMessage(error);
102   ReleaseKeyFile();
103   return false;
104 }
105 
Close()106 bool KeyFileStore::Close() {
107   bool success = Flush();
108   ReleaseKeyFile();
109   return success;
110 }
111 
Flush()112 bool KeyFileStore::Flush() {
113   CHECK(key_file_);
114   GError* error = nullptr;
115   gsize length = 0;
116   gchar* data = g_key_file_to_data(key_file_, &length, &error);
117 
118   bool success = true;
119   if (!data || error) {
120     LOG(ERROR) << "Failed to convert key file to string: "
121                << ConvertErrorToMessage(error);
122     success = false;
123   }
124   if (success) {
125     ScopedUmask owner_only_umask(~(S_IRUSR | S_IWUSR) & 0777);
126     success = base::ImportantFileWriter::WriteFileAtomically(path_, data);
127     if (!success) {
128       LOG(ERROR) << "Failed to store key file: " << path_.value();
129     }
130   }
131   g_free(data);
132   return success;
133 }
134 
MarkAsCorrupted()135 bool KeyFileStore::MarkAsCorrupted() {
136   LOG(INFO) << "In " << __func__ << " for " << path_.value();
137   string corrupted_path = path_.value() + kCorruptSuffix;
138   int ret =  rename(path_.value().c_str(), corrupted_path.c_str());
139   if (ret != 0) {
140     PLOG(ERROR) << "File rename failed";
141     return false;
142   }
143   return true;
144 }
145 
GetGroups() const146 set<string> KeyFileStore::GetGroups() const {
147   CHECK(key_file_);
148   gsize length = 0;
149   gchar** groups = g_key_file_get_groups(key_file_, &length);
150   if (!groups) {
151     LOG(ERROR) << "Unable to obtain groups.";
152     return set<string>();
153   }
154   set<string> group_set(groups, groups + length);
155   g_strfreev(groups);
156   return group_set;
157 }
158 
159 // Returns a set so that caller can easily test whether a particular group
160 // is contained within this collection.
GetGroupsWithKey(const string & key) const161 set<string> KeyFileStore::GetGroupsWithKey(const string& key) const {
162   set<string> groups = GetGroups();
163   set<string> groups_with_key;
164   for (const auto& group : groups) {
165     if (g_key_file_has_key(key_file_, group.c_str(), key.c_str(), nullptr)) {
166       groups_with_key.insert(group);
167     }
168   }
169   return groups_with_key;
170 }
171 
GetGroupsWithProperties(const KeyValueStore & properties) const172 set<string> KeyFileStore::GetGroupsWithProperties(
173      const KeyValueStore& properties) const {
174   set<string> groups = GetGroups();
175   set<string> groups_with_properties;
176   for (const auto& group : groups) {
177     if (DoesGroupMatchProperties(group, properties)) {
178       groups_with_properties.insert(group);
179     }
180   }
181   return groups_with_properties;
182 }
183 
ContainsGroup(const string & group) const184 bool KeyFileStore::ContainsGroup(const string& group) const {
185   CHECK(key_file_);
186   return g_key_file_has_group(key_file_, group.c_str());
187 }
188 
DeleteKey(const string & group,const string & key)189 bool KeyFileStore::DeleteKey(const string& group, const string& key) {
190   CHECK(key_file_);
191   GError* error = nullptr;
192   g_key_file_remove_key(key_file_, group.c_str(), key.c_str(), &error);
193   if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
194     LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
195                << ConvertErrorToMessage(error);
196     return false;
197   }
198   return true;
199 }
200 
DeleteGroup(const string & group)201 bool KeyFileStore::DeleteGroup(const string& group) {
202   CHECK(key_file_);
203   GError* error = nullptr;
204   g_key_file_remove_group(key_file_, group.c_str(), &error);
205   if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
206     LOG(ERROR) << "Failed to delete group " << group << ": "
207                << ConvertErrorToMessage(error);
208     return false;
209   }
210   return true;
211 }
212 
SetHeader(const string & header)213 bool KeyFileStore::SetHeader(const string& header) {
214   GError* error = nullptr;
215   g_key_file_set_comment(key_file_, nullptr, nullptr, header.c_str(), &error);
216   if (error) {
217     LOG(ERROR) << "Failed to to set header: "
218                << ConvertErrorToMessage(error);
219     return false;
220   }
221   return true;
222 }
223 
GetString(const string & group,const string & key,string * value) const224 bool KeyFileStore::GetString(const string& group,
225                              const string& key,
226                              string* value) const {
227   CHECK(key_file_);
228   GError* error = nullptr;
229   gchar* data =
230       g_key_file_get_string(key_file_, group.c_str(), key.c_str(), &error);
231   if (!data) {
232     string s = ConvertErrorToMessage(error);
233     SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
234     return false;
235   }
236   if (value) {
237     *value = data;
238   }
239   g_free(data);
240   return true;
241 }
242 
SetString(const string & group,const string & key,const string & value)243 bool KeyFileStore::SetString(const string& group,
244                              const string& key,
245                              const string& value) {
246   CHECK(key_file_);
247   g_key_file_set_string(key_file_, group.c_str(), key.c_str(), value.c_str());
248   return true;
249 }
250 
GetBool(const string & group,const string & key,bool * value) const251 bool KeyFileStore::GetBool(const string& group,
252                            const string& key,
253                            bool* value) const {
254   CHECK(key_file_);
255   GError* error = nullptr;
256   gboolean data =
257       g_key_file_get_boolean(key_file_, group.c_str(), key.c_str(), &error);
258   if (error) {
259     string s = ConvertErrorToMessage(error);
260     SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
261     return false;
262   }
263   if (value) {
264     *value = data;
265   }
266   return true;
267 }
268 
SetBool(const string & group,const string & key,bool value)269 bool KeyFileStore::SetBool(const string& group, const string& key, bool value) {
270   CHECK(key_file_);
271   g_key_file_set_boolean(key_file_,
272                          group.c_str(),
273                          key.c_str(),
274                          value ? TRUE : FALSE);
275   return true;
276 }
277 
GetInt(const string & group,const string & key,int * value) const278 bool KeyFileStore::GetInt(
279     const string& group, const string& key, int* value) const {
280   CHECK(key_file_);
281   GError* error = nullptr;
282   gint data =
283       g_key_file_get_integer(key_file_, group.c_str(), key.c_str(), &error);
284   if (error) {
285     string s = ConvertErrorToMessage(error);
286     SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
287     return false;
288   }
289   if (value) {
290     *value = data;
291   }
292   return true;
293 }
294 
SetInt(const string & group,const string & key,int value)295 bool KeyFileStore::SetInt(const string& group, const string& key, int value) {
296   CHECK(key_file_);
297   g_key_file_set_integer(key_file_, group.c_str(), key.c_str(), value);
298   return true;
299 }
300 
GetUint64(const string & group,const string & key,uint64_t * value) const301 bool KeyFileStore::GetUint64(
302     const string& group, const string& key, uint64_t* value) const {
303   // Read the value in as a string and then convert to uint64_t because glib's
304   // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms
305   // in unit tests.
306   string data_string;
307   if (!GetString(group, key, &data_string)) {
308     return false;
309   }
310 
311   uint64_t data;
312   if (!base::StringToUint64(data_string, &data)) {
313     SLOG(this, 10) << "Failed to convert (" << group << ":" << key << "): "
314                    << "string to uint64_t conversion failed";
315     return false;
316   }
317 
318   if (value) {
319     *value = data;
320   }
321 
322   return true;
323 }
324 
SetUint64(const string & group,const string & key,uint64_t value)325 bool KeyFileStore::SetUint64(
326     const string& group, const string& key, uint64_t value) {
327   // Convert the value to a string first, then save the value because glib's
328   // g_key_file_get_uint64 appears not to work on 32-bit platforms in our
329   // unit tests.
330   return SetString(group, key, base::Uint64ToString(value));
331 }
332 
GetStringList(const string & group,const string & key,vector<string> * value) const333 bool KeyFileStore::GetStringList(const string& group,
334                                  const string& key,
335                                  vector<string>* value) const {
336   CHECK(key_file_);
337   gsize length = 0;
338   GError* error = nullptr;
339   gchar** data = g_key_file_get_string_list(key_file_,
340                                             group.c_str(),
341                                             key.c_str(),
342                                             &length,
343                                             &error);
344   if (!data) {
345     string s = ConvertErrorToMessage(error);
346     SLOG(this, 10) << "Failed to lookup (" << group << ":" << key << "): " << s;
347     return false;
348   }
349   if (value) {
350     value->assign(data, data + length);
351   }
352   g_strfreev(data);
353   return true;
354 }
355 
SetStringList(const string & group,const string & key,const vector<string> & value)356 bool KeyFileStore::SetStringList(const string& group,
357                                  const string& key,
358                                  const vector<string>& value) {
359   CHECK(key_file_);
360   vector<const char*> list;
361   for (const auto& string_entry : value) {
362     list.push_back(string_entry.c_str());
363   }
364   g_key_file_set_string_list(key_file_,
365                              group.c_str(),
366                              key.c_str(),
367                              list.data(),
368                              list.size());
369   return true;
370 }
371 
GetCryptedString(const string & group,const string & key,string * value)372 bool KeyFileStore::GetCryptedString(const string& group,
373                                     const string& key,
374                                     string* value) {
375   if (!GetString(group, key, value)) {
376     return false;
377   }
378   if (value) {
379     *value = crypto_.Decrypt(*value);
380   }
381   return true;
382 }
383 
SetCryptedString(const string & group,const string & key,const string & value)384 bool KeyFileStore::SetCryptedString(const string& group,
385                                     const string& key,
386                                     const string& value) {
387   return SetString(group, key, crypto_.Encrypt(value));
388 }
389 
DoesGroupMatchProperties(const string & group,const KeyValueStore & properties) const390 bool KeyFileStore::DoesGroupMatchProperties(
391     const string& group, const KeyValueStore& properties) const {
392   for (const auto& property : properties.properties()) {
393     if (property.second.IsTypeCompatible<bool>()) {
394       bool value;
395       if (!GetBool(group, property.first, &value) ||
396           value != property.second.Get<bool>()) {
397         return false;
398       }
399     } else if (property.second.IsTypeCompatible<int32_t>()) {
400       int value;
401       if (!GetInt(group, property.first, &value) ||
402           value != property.second.Get<int32_t>()) {
403         return false;
404       }
405     } else if (property.second.IsTypeCompatible<string>()) {
406       string value;
407       if (!GetString(group, property.first, &value) ||
408           value != property.second.Get<string>()) {
409         return false;
410       }
411     }
412   }
413   return true;
414 }
415 
416 }  // namespace shill
417