Home | History | Annotate | Download | only in shill
      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;
     43 static string ObjectID(const KeyFileStore* k) { return "(key_file_store)"; }
     44 }
     45 
     46 namespace {
     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 
     60 KeyFileStore::KeyFileStore(const base::FilePath& path)
     61     : crypto_(),
     62       key_file_(nullptr),
     63       path_(path) {
     64   CHECK(!path_.empty());
     65 }
     66 
     67 KeyFileStore::~KeyFileStore() {
     68   ReleaseKeyFile();
     69 }
     70 
     71 void KeyFileStore::ReleaseKeyFile() {
     72   if (key_file_) {
     73     g_key_file_free(key_file_);
     74     key_file_ = nullptr;
     75   }
     76 }
     77 
     78 bool KeyFileStore::IsNonEmpty() const {
     79   int64_t file_size = 0;
     80   return base::GetFileSize(path_, &file_size) && file_size != 0;
     81 }
     82 
     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 
    106 bool KeyFileStore::Close() {
    107   bool success = Flush();
    108   ReleaseKeyFile();
    109   return success;
    110 }
    111 
    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 
    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 
    146 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.
    161 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 
    172 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 
    184 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 
    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 
    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 
    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 
    224 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 
    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 
    251 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 
    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 
    278 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 
    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 
    301 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 
    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 
    333 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 
    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 
    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 
    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 
    390 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