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