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