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