1 /*
2  * Copyright 2020 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 "storage/config_cache.h"
18 
19 #include <bluetooth/log.h>
20 
21 #include <ios>
22 #include <sstream>
23 #include <utility>
24 
25 #include "hci/enum_helper.h"
26 #include "os/parameter_provider.h"
27 #include "storage/mutation.h"
28 
29 namespace {
30 
31 const std::unordered_set<std::string_view> kEncryptKeyNameList = {
32     "LinkKey", "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_LID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
33 
TrimAfterNewLine(std::string & value)34 bool TrimAfterNewLine(std::string& value) {
35   std::string value_no_newline;
36   size_t newline_position = value.find_first_of('\n');
37   if (newline_position != std::string::npos) {
38     value.erase(newline_position);
39     return true;
40   }
41   return false;
42 }
43 
InEncryptKeyNameList(std::string key)44 bool InEncryptKeyNameList(std::string key) {
45   return kEncryptKeyNameList.find(key) != kEncryptKeyNameList.end();
46 }
47 
48 }  // namespace
49 
50 namespace bluetooth {
51 namespace storage {
52 
53 const std::unordered_set<std::string_view> kLePropertyNames = {
54     "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"};
55 
56 const std::unordered_set<std::string_view> kClassicPropertyNames = {
57     "LinkKey", "SdpDiMaufacturer", "SdpDiModel", "SdpDiHardwareVersion", "SdpDiVendorSource"};
58 
59 const std::string ConfigCache::kDefaultSectionName = "Global";
60 
61 std::string kEncryptedStr = "encrypted";
62 
ConfigCache(size_t temp_device_capacity,std::unordered_set<std::string_view> persistent_property_names)63 ConfigCache::ConfigCache(size_t temp_device_capacity, std::unordered_set<std::string_view> persistent_property_names)
64     : persistent_property_names_(std::move(persistent_property_names)),
65       information_sections_(),
66       persistent_devices_(),
67       temporary_devices_(temp_device_capacity) {}
68 
SetPersistentConfigChangedCallback(std::function<void ()> persistent_config_changed_callback)69 void ConfigCache::SetPersistentConfigChangedCallback(std::function<void()> persistent_config_changed_callback) {
70   std::lock_guard<std::recursive_mutex> lock(mutex_);
71   persistent_config_changed_callback_ = std::move(persistent_config_changed_callback);
72 }
73 
ConfigCache(ConfigCache && other)74 ConfigCache::ConfigCache(ConfigCache&& other) noexcept
75     : persistent_config_changed_callback_(nullptr),
76       persistent_property_names_(std::move(other.persistent_property_names_)),
77       information_sections_(std::move(other.information_sections_)),
78       persistent_devices_(std::move(other.persistent_devices_)),
79       temporary_devices_(std::move(other.temporary_devices_)) {
80   log::assert_that(
81       other.persistent_config_changed_callback_ == nullptr,
82       "Can't assign after setting the callback");
83 }
84 
operator =(ConfigCache && other)85 ConfigCache& ConfigCache::operator=(ConfigCache&& other) noexcept {
86   if (&other == this) {
87     return *this;
88   }
89   std::lock_guard<std::recursive_mutex> my_lock(mutex_);
90   std::lock_guard<std::recursive_mutex> others_lock(other.mutex_);
91   log::assert_that(
92       other.persistent_config_changed_callback_ == nullptr,
93       "Can't assign after setting the callback");
94   persistent_config_changed_callback_ = {};
95   persistent_property_names_ = std::move(other.persistent_property_names_);
96   information_sections_ = std::move(other.information_sections_);
97   persistent_devices_ = std::move(other.persistent_devices_);
98   temporary_devices_ = std::move(other.temporary_devices_);
99   return *this;
100 }
101 
operator ==(const ConfigCache & rhs) const102 bool ConfigCache::operator==(const ConfigCache& rhs) const {
103   std::lock_guard<std::recursive_mutex> my_lock(mutex_);
104   std::lock_guard<std::recursive_mutex> others_lock(rhs.mutex_);
105   return persistent_property_names_ == rhs.persistent_property_names_ &&
106          information_sections_ == rhs.information_sections_ && persistent_devices_ == rhs.persistent_devices_ &&
107          temporary_devices_ == rhs.temporary_devices_;
108 }
109 
operator !=(const ConfigCache & rhs) const110 bool ConfigCache::operator!=(const ConfigCache& rhs) const {
111   return !(*this == rhs);
112 }
113 
Clear()114 void ConfigCache::Clear() {
115   std::lock_guard<std::recursive_mutex> lock(mutex_);
116   if (information_sections_.size() > 0) {
117     information_sections_.clear();
118     PersistentConfigChangedCallback();
119   }
120   if (persistent_devices_.size() > 0) {
121     persistent_devices_.clear();
122     PersistentConfigChangedCallback();
123   }
124   if (temporary_devices_.size() > 0) {
125     temporary_devices_.clear();
126   }
127 }
128 
HasSection(const std::string & section) const129 bool ConfigCache::HasSection(const std::string& section) const {
130   std::lock_guard<std::recursive_mutex> lock(mutex_);
131   return information_sections_.contains(section) || persistent_devices_.contains(section) ||
132          temporary_devices_.contains(section);
133 }
134 
HasProperty(const std::string & section,const std::string & property) const135 bool ConfigCache::HasProperty(const std::string& section, const std::string& property) const {
136   std::lock_guard<std::recursive_mutex> lock(mutex_);
137   auto section_iter = information_sections_.find(section);
138   if (section_iter != information_sections_.end()) {
139     return section_iter->second.find(property) != section_iter->second.end();
140   }
141   section_iter = persistent_devices_.find(section);
142   if (section_iter != persistent_devices_.end()) {
143     return section_iter->second.find(property) != section_iter->second.end();
144   }
145   section_iter = temporary_devices_.find(section);
146   if (section_iter != temporary_devices_.end()) {
147     return section_iter->second.find(property) != section_iter->second.end();
148   }
149   return false;
150 }
151 
GetProperty(const std::string & section,const std::string & property) const152 std::optional<std::string> ConfigCache::GetProperty(const std::string& section, const std::string& property) const {
153   std::lock_guard<std::recursive_mutex> lock(mutex_);
154   auto section_iter = information_sections_.find(section);
155   if (section_iter != information_sections_.end()) {
156     auto property_iter = section_iter->second.find(property);
157     if (property_iter != section_iter->second.end()) {
158       return property_iter->second;
159     }
160   }
161   section_iter = persistent_devices_.find(section);
162   if (section_iter != persistent_devices_.end()) {
163     auto property_iter = section_iter->second.find(property);
164     if (property_iter != section_iter->second.end()) {
165       std::string value = property_iter->second;
166       if (os::ParameterProvider::GetBtKeystoreInterface() != nullptr && value == kEncryptedStr) {
167         return os::ParameterProvider::GetBtKeystoreInterface()->get_key(section + "-" + property);
168       }
169       return value;
170     }
171   }
172   section_iter = temporary_devices_.find(section);
173   if (section_iter != temporary_devices_.end()) {
174     auto property_iter = section_iter->second.find(property);
175     if (property_iter != section_iter->second.end()) {
176       return property_iter->second;
177     }
178   }
179   return std::nullopt;
180 }
181 
SetProperty(std::string section,std::string property,std::string value)182 void ConfigCache::SetProperty(std::string section, std::string property, std::string value) {
183   std::lock_guard<std::recursive_mutex> lock(mutex_);
184   TrimAfterNewLine(section);
185   TrimAfterNewLine(property);
186   TrimAfterNewLine(value);
187   log::assert_that(!section.empty(), "Empty section name not allowed");
188   log::assert_that(!property.empty(), "Empty property name not allowed");
189   if (!IsDeviceSection(section)) {
190     auto section_iter = information_sections_.find(section);
191     if (section_iter == information_sections_.end()) {
192       section_iter = information_sections_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
193     }
194     section_iter->second.insert_or_assign(property, std::move(value));
195     PersistentConfigChangedCallback();
196     return;
197   }
198   auto section_iter = persistent_devices_.find(section);
199   if (section_iter == persistent_devices_.end() && IsPersistentProperty(property)) {
200     // move paired devices or create new paired device when a link key is set
201     auto section_properties = temporary_devices_.extract(section);
202     if (section_properties) {
203       section_iter = persistent_devices_.try_emplace_back(section, std::move(section_properties->second)).first;
204     } else {
205       section_iter = persistent_devices_.try_emplace_back(section, common::ListMap<std::string, std::string>{}).first;
206     }
207   }
208   if (section_iter != persistent_devices_.end()) {
209     bool is_encrypted = value == kEncryptedStr;
210     if ((!value.empty()) && os::ParameterProvider::GetBtKeystoreInterface() != nullptr &&
211         os::ParameterProvider::IsCommonCriteriaMode() && InEncryptKeyNameList(property) && !is_encrypted) {
212       if (os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(
213               section + "-" + property, value)) {
214         value = kEncryptedStr;
215       }
216     }
217     section_iter->second.insert_or_assign(property, std::move(value));
218     PersistentConfigChangedCallback();
219     return;
220   }
221   section_iter = temporary_devices_.find(section);
222   if (section_iter == temporary_devices_.end()) {
223     auto triple = temporary_devices_.try_emplace(section, common::ListMap<std::string, std::string>{});
224     section_iter = std::get<0>(triple);
225   }
226   section_iter->second.insert_or_assign(property, std::move(value));
227 }
228 
RemoveSection(const std::string & section)229 bool ConfigCache::RemoveSection(const std::string& section) {
230   std::lock_guard<std::recursive_mutex> lock(mutex_);
231   // sections are unique among all three maps, hence removing from one of them is enough
232   if (information_sections_.extract(section) || persistent_devices_.extract(section)) {
233     PersistentConfigChangedCallback();
234     return true;
235   } else {
236     return temporary_devices_.extract(section).has_value();
237   }
238 }
239 
RemoveProperty(const std::string & section,const std::string & property)240 bool ConfigCache::RemoveProperty(const std::string& section, const std::string& property) {
241   std::lock_guard<std::recursive_mutex> lock(mutex_);
242   auto section_iter = information_sections_.find(section);
243   if (section_iter != information_sections_.end()) {
244     auto value = section_iter->second.extract(property);
245     // if section is empty after removal, remove the whole section as empty section is not allowed
246     if (section_iter->second.size() == 0) {
247       information_sections_.erase(section_iter);
248     }
249     if (value.has_value()) {
250       PersistentConfigChangedCallback();
251       return true;
252     } else {
253       return false;
254     }
255   }
256   section_iter = persistent_devices_.find(section);
257   if (section_iter != persistent_devices_.end()) {
258     auto value = section_iter->second.extract(property);
259     // if section is empty after removal, remove the whole section as empty section is not allowed
260     if (section_iter->second.size() == 0) {
261       persistent_devices_.erase(section_iter);
262     } else if (value && IsPersistentProperty(property)) {
263       // move unpaired device
264       auto section_properties = persistent_devices_.extract(section);
265       temporary_devices_.insert_or_assign(section, std::move(section_properties->second));
266     }
267     if (value.has_value()) {
268       PersistentConfigChangedCallback();
269       if (os::ParameterProvider::GetBtKeystoreInterface() != nullptr && os::ParameterProvider::IsCommonCriteriaMode() &&
270           InEncryptKeyNameList(property)) {
271         os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(section + "-" + property, "");
272       }
273       return true;
274     } else {
275       return false;
276     }
277   }
278   section_iter = temporary_devices_.find(section);
279   if (section_iter != temporary_devices_.end()) {
280     auto value = section_iter->second.extract(property);
281     if (section_iter->second.size() == 0) {
282       temporary_devices_.erase(section_iter);
283     }
284     return value.has_value();
285   }
286   return false;
287 }
288 
ConvertEncryptOrDecryptKeyIfNeeded()289 void ConfigCache::ConvertEncryptOrDecryptKeyIfNeeded() {
290   std::lock_guard<std::recursive_mutex> lock(mutex_);
291   log::info("");
292   auto persistent_sections = GetPersistentSections();
293   for (const auto& section : persistent_sections) {
294     auto section_iter = persistent_devices_.find(section);
295     for (const auto& property : kEncryptKeyNameList) {
296       auto property_iter = section_iter->second.find(std::string(property));
297       if (property_iter != section_iter->second.end()) {
298         bool is_encrypted = property_iter->second == kEncryptedStr;
299         if ((!property_iter->second.empty()) && os::ParameterProvider::GetBtKeystoreInterface() != nullptr &&
300             os::ParameterProvider::IsCommonCriteriaMode() && !is_encrypted) {
301           if (os::ParameterProvider::GetBtKeystoreInterface()->set_encrypt_key_or_remove_key(
302                   section + "-" + std::string(property), property_iter->second)) {
303             SetProperty(section, std::string(property), kEncryptedStr);
304           }
305         }
306         if (os::ParameterProvider::GetBtKeystoreInterface() != nullptr && is_encrypted) {
307           std::string value_str =
308               os::ParameterProvider::GetBtKeystoreInterface()->get_key(section + "-" + std::string(property));
309           if (!os::ParameterProvider::IsCommonCriteriaMode()) {
310             SetProperty(section, std::string(property), value_str);
311           }
312         }
313       }
314     }
315   }
316 }
317 
IsDeviceSection(const std::string & section)318 bool ConfigCache::IsDeviceSection(const std::string& section) {
319   return hci::Address::IsValidAddress(section);
320 }
321 
IsPersistentProperty(const std::string & property) const322 bool ConfigCache::IsPersistentProperty(const std::string& property) const {
323   return persistent_property_names_.find(property) != persistent_property_names_.end();
324 }
325 
RemoveSectionWithProperty(const std::string & property)326 void ConfigCache::RemoveSectionWithProperty(const std::string& property) {
327   std::lock_guard<std::recursive_mutex> lock(mutex_);
328   size_t num_persistent_removed = 0;
329   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
330     for (auto it = config_section->begin(); it != config_section->end();) {
331       if (it->second.contains(property)) {
332         log::info("Removing persistent section {} with property {}", it->first, property);
333         it = config_section->erase(it);
334         num_persistent_removed++;
335         continue;
336       }
337       it++;
338     }
339   }
340   for (auto it = temporary_devices_.begin(); it != temporary_devices_.end();) {
341     if (it->second.contains(property)) {
342       log::info("Removing temporary section {} with property {}", it->first, property);
343       it = temporary_devices_.erase(it);
344       continue;
345     }
346     it++;
347   }
348   if (num_persistent_removed > 0) {
349     PersistentConfigChangedCallback();
350   }
351 }
352 
GetPersistentSections() const353 std::vector<std::string> ConfigCache::GetPersistentSections() const {
354   std::lock_guard<std::recursive_mutex> lock(mutex_);
355   std::vector<std::string> paired_devices;
356   paired_devices.reserve(persistent_devices_.size());
357   for (const auto& elem : persistent_devices_) {
358     paired_devices.emplace_back(elem.first);
359   }
360   return paired_devices;
361 }
362 
Commit(std::queue<MutationEntry> & mutation_entries)363 void ConfigCache::Commit(std::queue<MutationEntry>& mutation_entries) {
364   std::lock_guard<std::recursive_mutex> lock(mutex_);
365   while (!mutation_entries.empty()) {
366     auto entry = std::move(mutation_entries.front());
367     mutation_entries.pop();
368     switch (entry.entry_type) {
369       case MutationEntry::EntryType::SET:
370         SetProperty(std::move(entry.section), std::move(entry.property), std::move(entry.value));
371         break;
372       case MutationEntry::EntryType::REMOVE_PROPERTY:
373         RemoveProperty(entry.section, entry.property);
374         break;
375       case MutationEntry::EntryType::REMOVE_SECTION:
376         RemoveSection(entry.section);
377         break;
378         // do not write a default case so that when a new enum is defined, compilation would fail automatically
379     }
380   }
381 }
382 
SerializeToLegacyFormat() const383 std::string ConfigCache::SerializeToLegacyFormat() const {
384   std::lock_guard<std::recursive_mutex> lock(mutex_);
385   std::stringstream serialized;
386   for (const auto* config_section : {&information_sections_, &persistent_devices_}) {
387     for (const auto& section : *config_section) {
388       serialized << "[" << section.first << "]" << std::endl;
389       for (const auto& property : section.second) {
390         serialized << property.first << " = " << property.second << std::endl;
391       }
392       serialized << std::endl;
393     }
394   }
395   return serialized.str();
396 }
397 
GetSectionNamesWithProperty(const std::string & property) const398 std::vector<ConfigCache::SectionAndPropertyValue> ConfigCache::GetSectionNamesWithProperty(
399     const std::string& property) const {
400   std::lock_guard<std::recursive_mutex> lock(mutex_);
401   std::vector<SectionAndPropertyValue> result;
402   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
403     for (const auto& elem : *config_section) {
404       auto it = elem.second.find(property);
405       if (it != elem.second.end()) {
406         result.emplace_back(SectionAndPropertyValue{.section = elem.first, .property = it->second});
407         continue;
408       }
409     }
410   }
411   for (const auto& elem : temporary_devices_) {
412     auto it = elem.second.find(property);
413     if (it != elem.second.end()) {
414       result.emplace_back(SectionAndPropertyValue{.section = elem.first, .property = it->second});
415       continue;
416     }
417   }
418   return result;
419 }
420 
GetPropertyNames(const std::string & section) const421 std::vector<std::string> ConfigCache::GetPropertyNames(const std::string& section) const {
422   std::lock_guard<std::recursive_mutex> lock(mutex_);
423 
424   std::vector<std::string> property_names;
425   auto ProcessSections = [&](const auto& sections) {
426     auto section_iter = sections.find(section);
427     if (section_iter != sections.end()) {
428       for (const auto& [property_name, value] : section_iter->second) {
429         property_names.emplace_back(property_name);
430       }
431       return true;
432     }
433     return false;
434   };
435 
436   // A section must exist in at most one map.
437   if (ProcessSections(information_sections_)) {
438     return property_names;
439   }
440   if (ProcessSections(persistent_devices_)) {
441     return property_names;
442   }
443   ProcessSections(temporary_devices_);
444   return property_names;
445 }
446 
447 namespace {
448 
FixDeviceTypeInconsistencyInSection(const std::string & section_name,common::ListMap<std::string,std::string> & device_section_entries)449 bool FixDeviceTypeInconsistencyInSection(
450     const std::string& section_name, common::ListMap<std::string, std::string>& device_section_entries) {
451   if (!hci::Address::IsValidAddress(section_name)) {
452     return false;
453   }
454   auto device_type_iter = device_section_entries.find("DevType");
455   if (device_type_iter != device_section_entries.end() &&
456       device_type_iter->second == std::to_string(hci::DeviceType::DUAL)) {
457     // We might only have one of classic/LE keys for a dual device, but it is still a dual device,
458     // so we should not change the DevType.
459     return false;
460   }
461 
462   // we will ignore the existing DevType, since it is not known to be a DUAL device so
463   // the keys we have should be sufficient to infer the correct DevType
464   bool is_le = false;
465   bool is_classic = false;
466   // default
467   hci::DeviceType device_type = hci::DeviceType::BR_EDR;
468   for (const auto& entry : device_section_entries) {
469     if (kLePropertyNames.find(entry.first) != kLePropertyNames.end()) {
470       is_le = true;
471     }
472     if (kClassicPropertyNames.find(entry.first) != kClassicPropertyNames.end()) {
473       is_classic = true;
474     }
475   }
476   if (is_classic && is_le) {
477     device_type = hci::DeviceType::DUAL;
478   } else if (is_classic) {
479     device_type = hci::DeviceType::BR_EDR;
480   } else if (is_le) {
481     device_type = hci::DeviceType::LE;
482   }
483   bool inconsistent = true;
484   std::string device_type_str = std::to_string(device_type);
485   if (device_type_iter != device_section_entries.end()) {
486     inconsistent = device_type_str != device_type_iter->second;
487     if (inconsistent) {
488       device_type_iter->second = std::move(device_type_str);
489     }
490   } else {
491     device_section_entries.insert_or_assign("DevType", std::move(device_type_str));
492   }
493   return inconsistent;
494 }
495 
496 }  // namespace
497 
FixDeviceTypeInconsistencies()498 bool ConfigCache::FixDeviceTypeInconsistencies() {
499   std::lock_guard<std::recursive_mutex> lock(mutex_);
500   bool persistent_device_changed = false;
501   for (auto* config_section : {&information_sections_, &persistent_devices_}) {
502     for (auto& elem : *config_section) {
503       if (FixDeviceTypeInconsistencyInSection(elem.first, elem.second)) {
504         persistent_device_changed = true;
505       }
506     }
507   }
508   bool temp_device_changed = false;
509   for (auto& elem : temporary_devices_) {
510     if (FixDeviceTypeInconsistencyInSection(elem.first, elem.second)) {
511       temp_device_changed = true;
512     }
513   }
514   if (persistent_device_changed) {
515     PersistentConfigChangedCallback();
516   }
517   return persistent_device_changed || temp_device_changed;
518 }
519 
HasAtLeastOneMatchingPropertiesInSection(const std::string & section,const std::unordered_set<std::string_view> & property_names) const520 bool ConfigCache::HasAtLeastOneMatchingPropertiesInSection(
521     const std::string& section, const std::unordered_set<std::string_view>& property_names) const {
522   std::lock_guard<std::recursive_mutex> lock(mutex_);
523   const common::ListMap<std::string, std::string>* section_ptr;
524   if (!IsDeviceSection(section)) {
525     auto section_iter = information_sections_.find(section);
526     if (section_iter == information_sections_.end()) {
527       return false;
528     }
529     section_ptr = &section_iter->second;
530   } else {
531     auto section_iter = persistent_devices_.find(section);
532     if (section_iter == persistent_devices_.end()) {
533       section_iter = temporary_devices_.find(section);
534       if (section_iter == temporary_devices_.end()) {
535         return false;
536       }
537     }
538     section_ptr = &section_iter->second;
539   }
540   for (const auto& property : *section_ptr) {
541     if (property_names.count(property.first) > 0) {
542       return true;
543     }
544   }
545   return false;
546 }
547 
IsPersistentSection(const std::string & section) const548 bool ConfigCache::IsPersistentSection(const std::string& section) const {
549   std::lock_guard<std::recursive_mutex> lock(mutex_);
550   return persistent_devices_.contains(section);
551 }
552 
553 }  // namespace storage
554 }  // namespace bluetooth
555