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