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