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 = §ion_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 = §ion_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