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