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