| /****************************************************************************** |
| * |
| * Copyright (C) 1999-2012 Broadcom Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| * This file contains functions for the Bluetooth Security Manager |
| * |
| ******************************************************************************/ |
| |
| #define LOG_TAG "bt_btm_sec" |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "device/include/controller.h" |
| #include "osi/include/log.h" |
| #include "osi/include/osi.h" |
| #include "osi/include/time.h" |
| |
| #include "bt_types.h" |
| #include "bt_utils.h" |
| #include "btm_int.h" |
| #include "btu.h" |
| #include "hcimsgs.h" |
| #include "l2c_int.h" |
| |
| #include "gatt_int.h" |
| |
| #define BTM_SEC_MAX_COLLISION_DELAY (5000) |
| |
| #ifdef APPL_AUTH_WRITE_EXCEPTION |
| bool(APPL_AUTH_WRITE_EXCEPTION)(const RawAddress& bd_addr); |
| #endif |
| |
| /******************************************************************************* |
| * L O C A L F U N C T I O N P R O T O T Y P E S * |
| ******************************************************************************/ |
| tBTM_SEC_SERV_REC* btm_sec_find_first_serv(bool is_originator, uint16_t psm); |
| static tBTM_SEC_SERV_REC* btm_sec_find_next_serv(tBTM_SEC_SERV_REC* p_cur); |
| static tBTM_SEC_SERV_REC* btm_sec_find_mx_serv(uint8_t is_originator, |
| uint16_t psm, |
| uint32_t mx_proto_id, |
| uint32_t mx_chan_id); |
| |
| static tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec); |
| static bool btm_sec_start_get_name(tBTM_SEC_DEV_REC* p_dev_rec); |
| static void btm_sec_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec); |
| static void btm_sec_start_encryption(tBTM_SEC_DEV_REC* p_dev_rec); |
| static void btm_sec_collision_timeout(void* data); |
| static void btm_restore_mode(void); |
| static void btm_sec_pairing_timeout(void* data); |
| static tBTM_STATUS btm_sec_dd_create_conn(tBTM_SEC_DEV_REC* p_dev_rec); |
| static void btm_sec_change_pairing_state(tBTM_PAIRING_STATE new_state); |
| |
| static const char* btm_pair_state_descr(tBTM_PAIRING_STATE state); |
| |
| static void btm_sec_check_pending_reqs(void); |
| static bool btm_sec_queue_mx_request(const RawAddress& bd_addr, uint16_t psm, |
| bool is_orig, uint32_t mx_proto_id, |
| uint32_t mx_chan_id, |
| tBTM_SEC_CALLBACK* p_callback, |
| void* p_ref_data); |
| static void btm_sec_bond_cancel_complete(void); |
| static void btm_send_link_key_notif(tBTM_SEC_DEV_REC* p_dev_rec); |
| static bool btm_sec_check_prefetch_pin(tBTM_SEC_DEV_REC* p_dev_rec); |
| |
| static uint8_t btm_sec_start_authorization(tBTM_SEC_DEV_REC* p_dev_rec); |
| bool btm_sec_are_all_trusted(uint32_t p_mask[]); |
| |
| static tBTM_STATUS btm_sec_send_hci_disconnect(tBTM_SEC_DEV_REC* p_dev_rec, |
| uint8_t reason, |
| uint16_t conn_handle); |
| uint8_t btm_sec_start_role_switch(tBTM_SEC_DEV_REC* p_dev_rec); |
| tBTM_SEC_DEV_REC* btm_sec_find_dev_by_sec_state(uint8_t state); |
| |
| static bool btm_sec_set_security_level(CONNECTION_TYPE conn_type, |
| const char* p_name, uint8_t service_id, |
| uint16_t sec_level, uint16_t psm, |
| uint32_t mx_proto_id, |
| uint32_t mx_chan_id); |
| |
| static bool btm_dev_authenticated(tBTM_SEC_DEV_REC* p_dev_rec); |
| static bool btm_dev_encrypted(tBTM_SEC_DEV_REC* p_dev_rec); |
| static bool btm_dev_authorized(tBTM_SEC_DEV_REC* p_dev_rec); |
| static bool btm_serv_trusted(tBTM_SEC_DEV_REC* p_dev_rec, |
| tBTM_SEC_SERV_REC* p_serv_rec); |
| static bool btm_sec_is_serv_level0(uint16_t psm); |
| static uint16_t btm_sec_set_serv_level4_flags(uint16_t cur_security, |
| bool is_originator); |
| |
| static bool btm_sec_queue_encrypt_request(const RawAddress& bd_addr, |
| tBT_TRANSPORT transport, |
| tBTM_SEC_CALLBACK* p_callback, |
| void* p_ref_data, |
| tBTM_BLE_SEC_ACT sec_act); |
| static void btm_sec_check_pending_enc_req(tBTM_SEC_DEV_REC* p_dev_rec, |
| tBT_TRANSPORT transport, |
| uint8_t encr_enable); |
| |
| static bool btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC* p_dev_rec); |
| static bool btm_sec_is_master(tBTM_SEC_DEV_REC* p_dev_rec); |
| |
| /* true - authenticated link key is possible */ |
| static const bool btm_sec_io_map[BTM_IO_CAP_MAX][BTM_IO_CAP_MAX] = { |
| /* OUT, IO, IN, NONE */ |
| /* OUT */ {false, false, true, false}, |
| /* IO */ {false, true, true, false}, |
| /* IN */ {true, true, true, false}, |
| /* NONE */ {false, false, false, false}}; |
| /* BTM_IO_CAP_OUT 0 DisplayOnly */ |
| /* BTM_IO_CAP_IO 1 DisplayYesNo */ |
| /* BTM_IO_CAP_IN 2 KeyboardOnly */ |
| /* BTM_IO_CAP_NONE 3 NoInputNoOutput */ |
| |
| /******************************************************************************* |
| * |
| * Function btm_dev_authenticated |
| * |
| * Description check device is authenticated |
| * |
| * Returns bool true or false |
| * |
| ******************************************************************************/ |
| static bool btm_dev_authenticated(tBTM_SEC_DEV_REC* p_dev_rec) { |
| if (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) { |
| return (true); |
| } |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_dev_encrypted |
| * |
| * Description check device is encrypted |
| * |
| * Returns bool true or false |
| * |
| ******************************************************************************/ |
| static bool btm_dev_encrypted(tBTM_SEC_DEV_REC* p_dev_rec) { |
| if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) { |
| return (true); |
| } |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_dev_authorized |
| * |
| * Description check device is authorized |
| * |
| * Returns bool true or false |
| * |
| ******************************************************************************/ |
| static bool btm_dev_authorized(tBTM_SEC_DEV_REC* p_dev_rec) { |
| if (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) { |
| return (true); |
| } |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_dev_16_digit_authenticated |
| * |
| * Description check device is authenticated by using 16 digit pin or MITM |
| * |
| * Returns bool true or false |
| * |
| ******************************************************************************/ |
| static bool btm_dev_16_digit_authenticated(tBTM_SEC_DEV_REC* p_dev_rec) { |
| // BTM_SEC_16_DIGIT_PIN_AUTHED is set if MITM or 16 digit pin is used |
| if (p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) { |
| return (true); |
| } |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_serv_trusted |
| * |
| * Description check service is trusted |
| * |
| * Returns bool true or false |
| * |
| ******************************************************************************/ |
| static bool btm_serv_trusted(tBTM_SEC_DEV_REC* p_dev_rec, |
| tBTM_SEC_SERV_REC* p_serv_rec) { |
| if (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, |
| p_serv_rec->service_id)) { |
| return (true); |
| } |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecRegister |
| * |
| * Description Application manager calls this function to register for |
| * security services. There can be one and only one |
| * application saving link keys. BTM allows only first |
| * registration. |
| * |
| * Returns true if registered OK, else false |
| * |
| ******************************************************************************/ |
| bool BTM_SecRegister(tBTM_APPL_INFO* p_cb_info) { |
| BT_OCTET16 temp_value = {0}; |
| |
| BTM_TRACE_EVENT("%s application registered", __func__); |
| |
| LOG_INFO(LOG_TAG, "%s p_cb_info->p_le_callback == 0x%p", __func__, |
| p_cb_info->p_le_callback); |
| if (p_cb_info->p_le_callback) { |
| BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )", __func__); |
| SMP_Register(btm_proc_smp_cback); |
| /* if no IR is loaded, need to regenerate all the keys */ |
| if (memcmp(btm_cb.devcb.id_keys.ir, &temp_value, sizeof(BT_OCTET16)) == 0) { |
| btm_ble_reset_id(); |
| } |
| } else { |
| LOG_WARN(LOG_TAG, "%s p_cb_info->p_le_callback == NULL", __func__); |
| } |
| |
| btm_cb.api = *p_cb_info; |
| LOG_INFO(LOG_TAG, "%s btm_cb.api.p_le_callback = 0x%p ", __func__, |
| btm_cb.api.p_le_callback); |
| BTM_TRACE_EVENT("%s application registered", __func__); |
| return (true); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecRegisterLinkKeyNotificationCallback |
| * |
| * Description Application manager calls this function to register for |
| * link key notification. When there is nobody registered |
| * we should avoid changing link key |
| * |
| * Returns true if registered OK, else false |
| * |
| ******************************************************************************/ |
| bool BTM_SecRegisterLinkKeyNotificationCallback( |
| tBTM_LINK_KEY_CALLBACK* p_callback) { |
| btm_cb.api.p_link_key_callback = p_callback; |
| return true; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecAddRmtNameNotifyCallback |
| * |
| * Description Any profile can register to be notified when name of the |
| * remote device is resolved. |
| * |
| * Returns true if registered OK, else false |
| * |
| ******************************************************************************/ |
| bool BTM_SecAddRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback) { |
| int i; |
| |
| for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { |
| if (btm_cb.p_rmt_name_callback[i] == NULL) { |
| btm_cb.p_rmt_name_callback[i] = p_callback; |
| return (true); |
| } |
| } |
| |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecDeleteRmtNameNotifyCallback |
| * |
| * Description Any profile can deregister notification when a new Link Key |
| * is generated per connection. |
| * |
| * Returns true if OK, else false |
| * |
| ******************************************************************************/ |
| bool BTM_SecDeleteRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback) { |
| int i; |
| |
| for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { |
| if (btm_cb.p_rmt_name_callback[i] == p_callback) { |
| btm_cb.p_rmt_name_callback[i] = NULL; |
| return (true); |
| } |
| } |
| |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_GetSecurityFlags |
| * |
| * Description Get security flags for the device |
| * |
| * Returns bool true or false is device found |
| * |
| ******************************************************************************/ |
| bool BTM_GetSecurityFlags(const RawAddress& bd_addr, uint8_t* p_sec_flags) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| p_dev_rec = btm_find_dev(bd_addr); |
| if (p_dev_rec != NULL) { |
| *p_sec_flags = (uint8_t)p_dev_rec->sec_flags; |
| return (true); |
| } |
| BTM_TRACE_ERROR("BTM_GetSecurityFlags false"); |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_GetSecurityFlagsByTransport |
| * |
| * Description Get security flags for the device on a particular transport |
| * |
| * Returns bool true or false is device found |
| * |
| ******************************************************************************/ |
| bool BTM_GetSecurityFlagsByTransport(const RawAddress& bd_addr, |
| uint8_t* p_sec_flags, |
| tBT_TRANSPORT transport) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| p_dev_rec = btm_find_dev(bd_addr); |
| if (p_dev_rec != NULL) { |
| if (transport == BT_TRANSPORT_BR_EDR) |
| *p_sec_flags = (uint8_t)p_dev_rec->sec_flags; |
| else |
| *p_sec_flags = (uint8_t)(p_dev_rec->sec_flags >> 8); |
| |
| return (true); |
| } |
| BTM_TRACE_ERROR("BTM_GetSecurityFlags false"); |
| return (false); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SetPinType |
| * |
| * Description Set PIN type for the device. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void BTM_SetPinType(uint8_t pin_type, PIN_CODE pin_code, uint8_t pin_code_len) { |
| BTM_TRACE_API( |
| "BTM_SetPinType: pin type %d [variable-0, fixed-1], code %s, length %d", |
| pin_type, (char*)pin_code, pin_code_len); |
| |
| /* If device is not up security mode will be set as a part of startup */ |
| if ((btm_cb.cfg.pin_type != pin_type) && |
| controller_get_interface()->get_is_ready()) { |
| btsnd_hcic_write_pin_type(pin_type); |
| } |
| |
| btm_cb.cfg.pin_type = pin_type; |
| btm_cb.cfg.pin_code_len = pin_code_len; |
| memcpy(btm_cb.cfg.pin_code, pin_code, pin_code_len); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SetPairableMode |
| * |
| * Description Enable or disable pairing |
| * |
| * Parameters allow_pairing - (true or false) whether or not the device |
| * allows pairing. |
| * connect_only_paired - (true or false) whether or not to |
| * only allow paired devices to connect. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void BTM_SetPairableMode(bool allow_pairing, bool connect_only_paired) { |
| BTM_TRACE_API( |
| "BTM_SetPairableMode() allow_pairing: %u connect_only_paired: %u", |
| allow_pairing, connect_only_paired); |
| |
| btm_cb.pairing_disabled = !allow_pairing; |
| btm_cb.connect_only_paired = connect_only_paired; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SetSecureConnectionsOnly |
| * |
| * Description Enable or disable default treatment for Mode 4 Level 0 |
| * services |
| * |
| * Parameter secure_connections_only_mode - |
| * true means that the device should treat Mode 4 Level 0 |
| * services as services of other levels. |
| * false means that the device should provide default |
| * treatment for Mode 4 Level 0 services. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode) { |
| BTM_TRACE_API("%s: Mode : %u", __func__, secure_connections_only_mode); |
| |
| btm_cb.devcb.secure_connections_only = secure_connections_only_mode; |
| btm_cb.security_mode = BTM_SEC_MODE_SC; |
| } |
| #define BTM_NO_AVAIL_SEC_SERVICES ((uint16_t)0xffff) |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SetSecurityLevel |
| * |
| * Description Register service security level with Security Manager |
| * |
| * Parameters: is_originator - true if originating the connection |
| * p_name - Name of the service relevant only if |
| * authorization will show this name to user. |
| * Ignored if BTM_SEC_SERVICE_NAME_LEN is 0. |
| * service_id - service ID for the service passed to |
| * authorization callback |
| * sec_level - bit mask of the security features |
| * psm - L2CAP PSM |
| * mx_proto_id - protocol ID of multiplexing proto below |
| * mx_chan_id - channel ID of multiplexing proto below |
| * |
| * Returns true if registered OK, else false |
| * |
| ******************************************************************************/ |
| bool BTM_SetSecurityLevel(bool is_originator, const char* p_name, |
| uint8_t service_id, uint16_t sec_level, uint16_t psm, |
| uint32_t mx_proto_id, uint32_t mx_chan_id) { |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| CONNECTION_TYPE conn_type; |
| |
| if (is_originator) |
| conn_type = CONN_ORIENT_ORIG; |
| else |
| conn_type = CONN_ORIENT_TERM; |
| |
| return (btm_sec_set_security_level(conn_type, p_name, service_id, sec_level, |
| psm, mx_proto_id, mx_chan_id)); |
| #else |
| return (btm_sec_set_security_level(is_originator, p_name, service_id, |
| sec_level, psm, mx_proto_id, mx_chan_id)); |
| #endif |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_set_security_level |
| * |
| * Description Register service security level with Security Manager |
| * |
| * Parameters: conn_type - true if originating the connection |
| * p_name - Name of the service relevant only if |
| * authorization will show this name to user. |
| * Ignored if BTM_SEC_SERVICE_NAME_LEN is 0. |
| * service_id - service ID for the service passed to |
| * authorization callback |
| * sec_level - bit mask of the security features |
| * psm - L2CAP PSM |
| * mx_proto_id - protocol ID of multiplexing proto below |
| * mx_chan_id - channel ID of multiplexing proto below |
| * |
| * Returns true if registered OK, else false |
| * |
| ******************************************************************************/ |
| static bool btm_sec_set_security_level(CONNECTION_TYPE conn_type, |
| const char* p_name, uint8_t service_id, |
| uint16_t sec_level, uint16_t psm, |
| uint32_t mx_proto_id, |
| uint32_t mx_chan_id) { |
| tBTM_SEC_SERV_REC* p_srec; |
| uint16_t index; |
| uint16_t first_unused_record = BTM_NO_AVAIL_SEC_SERVICES; |
| bool record_allocated = false; |
| bool is_originator; |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| bool is_ucd; |
| |
| if (conn_type & CONNECTION_TYPE_ORIG_MASK) |
| is_originator = true; |
| else |
| is_originator = false; |
| |
| if (conn_type & CONNECTION_TYPE_CONNLESS_MASK) { |
| is_ucd = true; |
| } else { |
| is_ucd = false; |
| } |
| #else |
| is_originator = conn_type; |
| #endif |
| |
| BTM_TRACE_API("%s : sec: 0x%x", __func__, sec_level); |
| |
| /* See if the record can be reused (same service name, psm, mx_proto_id, |
| service_id, and mx_chan_id), or obtain the next unused record */ |
| |
| p_srec = &btm_cb.sec_serv_rec[0]; |
| |
| for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++) { |
| /* Check if there is already a record for this service */ |
| if (p_srec->security_flags & BTM_SEC_IN_USE) { |
| #if BTM_SEC_SERVICE_NAME_LEN > 0 |
| if (p_srec->psm == psm && p_srec->mx_proto_id == mx_proto_id && |
| service_id == p_srec->service_id && p_name && |
| (!strncmp(p_name, (char*)p_srec->orig_service_name, |
| /* strlcpy replaces end char with termination char*/ |
| BTM_SEC_SERVICE_NAME_LEN - 1) || |
| !strncmp(p_name, (char*)p_srec->term_service_name, |
| /* strlcpy replaces end char with termination char*/ |
| BTM_SEC_SERVICE_NAME_LEN - 1))) |
| #else |
| if (p_srec->psm == psm && p_srec->mx_proto_id == mx_proto_id && |
| service_id == p_srec->service_id) |
| #endif |
| { |
| record_allocated = true; |
| break; |
| } |
| } |
| /* Mark the first available service record */ |
| else if (!record_allocated) { |
| memset(p_srec, 0, sizeof(tBTM_SEC_SERV_REC)); |
| record_allocated = true; |
| first_unused_record = index; |
| } |
| } |
| |
| if (!record_allocated) { |
| BTM_TRACE_WARNING("BTM_SEC_REG: Out of Service Records (%d)", |
| BTM_SEC_MAX_SERVICE_RECORDS); |
| return (record_allocated); |
| } |
| |
| /* Process the request if service record is valid */ |
| /* If a duplicate service wasn't found, use the first available */ |
| if (index >= BTM_SEC_MAX_SERVICE_RECORDS) { |
| index = first_unused_record; |
| p_srec = &btm_cb.sec_serv_rec[index]; |
| } |
| |
| p_srec->psm = psm; |
| p_srec->service_id = service_id; |
| p_srec->mx_proto_id = mx_proto_id; |
| |
| if (is_originator) { |
| p_srec->orig_mx_chan_id = mx_chan_id; |
| #if BTM_SEC_SERVICE_NAME_LEN > 0 |
| strlcpy((char*)p_srec->orig_service_name, p_name, |
| BTM_SEC_SERVICE_NAME_LEN + 1); |
| #endif |
| /* clear out the old setting, just in case it exists */ |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (is_ucd) { |
| p_srec->ucd_security_flags &= ~( |
| BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | |
| BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | BTM_SEC_FORCE_MASTER | |
| BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); |
| } else |
| #endif |
| { |
| p_srec->security_flags &= ~( |
| BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | |
| BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | BTM_SEC_FORCE_MASTER | |
| BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); |
| } |
| |
| /* Parameter validation. Originator should not set requirements for |
| * incoming connections */ |
| sec_level &= |
| ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | |
| BTM_SEC_IN_MITM | BTM_SEC_IN_MIN_16_DIGIT_PIN); |
| |
| if (btm_cb.security_mode == BTM_SEC_MODE_SP || |
| btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || |
| btm_cb.security_mode == BTM_SEC_MODE_SC) { |
| if (sec_level & BTM_SEC_OUT_AUTHENTICATE) sec_level |= BTM_SEC_OUT_MITM; |
| } |
| |
| /* Make sure the authenticate bit is set, when encrypt bit is set */ |
| if (sec_level & BTM_SEC_OUT_ENCRYPT) sec_level |= BTM_SEC_OUT_AUTHENTICATE; |
| |
| /* outgoing connections usually set the security level right before |
| * the connection is initiated. |
| * set it to be the outgoing service */ |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (is_ucd == false) |
| #endif |
| { |
| btm_cb.p_out_serv = p_srec; |
| } |
| } else { |
| p_srec->term_mx_chan_id = mx_chan_id; |
| #if BTM_SEC_SERVICE_NAME_LEN > 0 |
| strlcpy((char*)p_srec->term_service_name, p_name, |
| BTM_SEC_SERVICE_NAME_LEN + 1); |
| #endif |
| /* clear out the old setting, just in case it exists */ |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (is_ucd) { |
| p_srec->ucd_security_flags &= |
| ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | |
| BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | BTM_SEC_FORCE_MASTER | |
| BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | |
| BTM_SEC_ATTEMPT_SLAVE | BTM_SEC_IN_MIN_16_DIGIT_PIN); |
| } else |
| #endif |
| { |
| p_srec->security_flags &= |
| ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | |
| BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | BTM_SEC_FORCE_MASTER | |
| BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | |
| BTM_SEC_ATTEMPT_SLAVE | BTM_SEC_IN_MIN_16_DIGIT_PIN); |
| } |
| |
| /* Parameter validation. Acceptor should not set requirements for outgoing |
| * connections */ |
| sec_level &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | |
| BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM); |
| |
| if (btm_cb.security_mode == BTM_SEC_MODE_SP || |
| btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || |
| btm_cb.security_mode == BTM_SEC_MODE_SC) { |
| if (sec_level & BTM_SEC_IN_AUTHENTICATE) sec_level |= BTM_SEC_IN_MITM; |
| } |
| |
| /* Make sure the authenticate bit is set, when encrypt bit is set */ |
| if (sec_level & BTM_SEC_IN_ENCRYPT) sec_level |= BTM_SEC_IN_AUTHENTICATE; |
| } |
| |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (is_ucd) { |
| p_srec->security_flags |= (uint16_t)(BTM_SEC_IN_USE); |
| p_srec->ucd_security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE); |
| } else { |
| p_srec->security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE); |
| } |
| |
| BTM_TRACE_API( |
| "BTM_SEC_REG[%d]: id %d, conn_type 0x%x, psm 0x%04x, proto_id %d, " |
| "chan_id %d", |
| index, service_id, conn_type, psm, mx_proto_id, mx_chan_id); |
| |
| BTM_TRACE_API( |
| " : security_flags: 0x%04x, ucd_security_flags: 0x%04x", |
| p_srec->security_flags, p_srec->ucd_security_flags); |
| |
| #if BTM_SEC_SERVICE_NAME_LEN > 0 |
| BTM_TRACE_API(" : service name [%s] (up to %d chars saved)", |
| p_name, BTM_SEC_SERVICE_NAME_LEN); |
| #endif |
| #else |
| p_srec->security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE); |
| |
| BTM_TRACE_API( |
| "BTM_SEC_REG[%d]: id %d, is_orig %d, psm 0x%04x, proto_id %d, chan_id %d", |
| index, service_id, is_originator, psm, mx_proto_id, mx_chan_id); |
| |
| #if BTM_SEC_SERVICE_NAME_LEN > 0 |
| BTM_TRACE_API( |
| " : sec: 0x%x, service name [%s] (up to %d chars saved)", |
| p_srec->security_flags, p_name, BTM_SEC_SERVICE_NAME_LEN); |
| #endif |
| #endif |
| |
| return (record_allocated); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecClrService |
| * |
| * Description Removes specified service record(s) from the security |
| * database. All service records with the specified name are |
| * removed. Typically used only by devices with limited RAM so |
| * that it can reuse an old security service record. |
| * |
| * Note: Unpredictable results may occur if a service is |
| * cleared that is still in use by an application/profile. |
| * |
| * Parameters Service ID - Id of the service to remove. '0' removes all |
| * service records (except SDP). |
| * |
| * Returns Number of records that were freed. |
| * |
| ******************************************************************************/ |
| uint8_t BTM_SecClrService(uint8_t service_id) { |
| tBTM_SEC_SERV_REC* p_srec = &btm_cb.sec_serv_rec[0]; |
| uint8_t num_freed = 0; |
| int i; |
| |
| for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) { |
| /* Delete services with specified name (if in use and not SDP) */ |
| if ((p_srec->security_flags & BTM_SEC_IN_USE) && |
| (p_srec->psm != BT_PSM_SDP) && |
| (!service_id || (service_id == p_srec->service_id))) { |
| BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d", i, service_id); |
| p_srec->security_flags = 0; |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| p_srec->ucd_security_flags = 0; |
| #endif |
| num_freed++; |
| } |
| } |
| |
| return (num_freed); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_clr_service_by_psm |
| * |
| * Description Removes specified service record from the security database. |
| * All service records with the specified psm are removed. |
| * Typically used by L2CAP to free up the service record used |
| * by dynamic PSM clients when the channel is closed. |
| * The given psm must be a virtual psm. |
| * |
| * Parameters Service ID - Id of the service to remove. '0' removes all |
| * service records (except SDP). |
| * |
| * Returns Number of records that were freed. |
| * |
| ******************************************************************************/ |
| uint8_t btm_sec_clr_service_by_psm(uint16_t psm) { |
| tBTM_SEC_SERV_REC* p_srec = &btm_cb.sec_serv_rec[0]; |
| uint8_t num_freed = 0; |
| int i; |
| |
| for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) { |
| /* Delete services with specified name (if in use and not SDP) */ |
| if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm)) { |
| BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d ", i, p_srec->service_id); |
| p_srec->security_flags = 0; |
| num_freed++; |
| } |
| } |
| BTM_TRACE_API("btm_sec_clr_service_by_psm psm:0x%x num_freed:%d", psm, |
| num_freed); |
| |
| return (num_freed); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_clr_temp_auth_service |
| * |
| * Description Removes specified device record's temporary authorization |
| * flag from the security database. |
| * |
| * Parameters Device address to be cleared |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| void btm_sec_clr_temp_auth_service(const RawAddress& bda) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| p_dev_rec = btm_find_dev(bda); |
| if (p_dev_rec == NULL) { |
| BTM_TRACE_WARNING("btm_sec_clr_temp_auth_service() - no dev CB"); |
| return; |
| } |
| |
| /* Reset the temporary authorized flag so that next time (untrusted) service |
| * is accessed autorization will take place */ |
| if (p_dev_rec->last_author_service_id != BTM_SEC_NO_LAST_SERVICE_ID && |
| p_dev_rec->p_cur_service) { |
| VLOG(1) << __func__ << " clearing device: " << bda; |
| |
| p_dev_rec->last_author_service_id = BTM_SEC_NO_LAST_SERVICE_ID; |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_PINCodeReply |
| * |
| * Description This function is called after Security Manager submitted |
| * PIN code request to the UI. |
| * |
| * Parameters: bd_addr - Address of the device for which PIN was |
| * requested |
| * res - result of the operation BTM_SUCCESS |
| * if success |
| * pin_len - length in bytes of the PIN Code |
| * p_pin - pointer to array with the PIN Code |
| * trusted_mask - bitwise OR of trusted services |
| * (array of uint32_t) |
| * |
| ******************************************************************************/ |
| void BTM_PINCodeReply(const RawAddress& bd_addr, uint8_t res, uint8_t pin_len, |
| uint8_t* p_pin, uint32_t trusted_mask[]) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| BTM_TRACE_API( |
| "BTM_PINCodeReply(): PairState: %s PairFlags: 0x%02x PinLen:%d " |
| "Result:%d", |
| btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, pin_len, |
| res); |
| |
| /* If timeout already expired or has been canceled, ignore the reply */ |
| if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) { |
| BTM_TRACE_WARNING("BTM_PINCodeReply() - Wrong State: %d", |
| btm_cb.pairing_state); |
| return; |
| } |
| |
| if (bd_addr != btm_cb.pairing_bda) { |
| BTM_TRACE_ERROR("BTM_PINCodeReply() - Wrong BD Addr"); |
| return; |
| } |
| |
| p_dev_rec = btm_find_dev(bd_addr); |
| if (p_dev_rec == NULL) { |
| BTM_TRACE_ERROR("BTM_PINCodeReply() - no dev CB"); |
| return; |
| } |
| |
| if ((pin_len > PIN_CODE_LEN) || (pin_len == 0) || (p_pin == NULL)) |
| res = BTM_ILLEGAL_VALUE; |
| |
| if (res != BTM_SUCCESS) { |
| /* if peer started dd OR we started dd and pre-fetch pin was not used send |
| * negative reply */ |
| if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) || |
| ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && |
| (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE))) { |
| /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed |
| * event */ |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); |
| btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; |
| |
| btsnd_hcic_pin_code_neg_reply(bd_addr); |
| } else { |
| p_dev_rec->security_required = BTM_SEC_NONE; |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| } |
| return; |
| } |
| if (trusted_mask) |
| BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); |
| p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; |
| p_dev_rec->pin_code_length = pin_len; |
| if (pin_len >= 16) { |
| p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; |
| } |
| |
| if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && |
| (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) && |
| (btm_cb.security_mode_changed == false)) { |
| /* This is start of the dedicated bonding if local device is 2.0 */ |
| btm_cb.pin_code_len = pin_len; |
| memcpy(btm_cb.pin_code, p_pin, pin_len); |
| |
| btm_cb.security_mode_changed = true; |
| #ifdef APPL_AUTH_WRITE_EXCEPTION |
| if (!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) |
| #endif |
| btsnd_hcic_write_auth_enable(true); |
| |
| btm_cb.acl_disc_reason = 0xff; |
| |
| /* if we rejected incoming connection request, we have to wait |
| * HCI_Connection_Complete event */ |
| /* before originating */ |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) { |
| BTM_TRACE_WARNING( |
| "BTM_PINCodeReply(): waiting HCI_Connection_Complete after rejected " |
| "incoming connection"); |
| /* we change state little bit early so btm_sec_connected() will originate |
| * connection */ |
| /* when existing ACL link is down completely */ |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ); |
| } |
| /* if we already accepted incoming connection from pairing device */ |
| else if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) { |
| BTM_TRACE_WARNING( |
| "BTM_PINCodeReply(): link is connecting so wait pin code request " |
| "from peer"); |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ); |
| } else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) { |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; |
| |
| if (btm_cb.api.p_auth_complete_callback) |
| (*btm_cb.api.p_auth_complete_callback)( |
| p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, |
| HCI_ERR_AUTH_FAILURE); |
| } |
| return; |
| } |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); |
| btm_cb.acl_disc_reason = HCI_SUCCESS; |
| |
| btsnd_hcic_pin_code_req_reply(bd_addr, pin_len, p_pin); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_bond_by_transport |
| * |
| * Description this is the bond function that will start either SSP or SMP. |
| * |
| * Parameters: bd_addr - Address of the device to bond |
| * pin_len - length in bytes of the PIN Code |
| * p_pin - pointer to array with the PIN Code |
| * trusted_mask - bitwise OR of trusted services |
| * (array of uint32_t) |
| * |
| * Note: After 2.1 parameters are not used and preserved here not to change API |
| ******************************************************************************/ |
| tBTM_STATUS btm_sec_bond_by_transport(const RawAddress& bd_addr, |
| tBT_TRANSPORT transport, uint8_t pin_len, |
| uint8_t* p_pin, uint32_t trusted_mask[]) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| tBTM_STATUS status; |
| uint8_t* p_features; |
| uint8_t ii; |
| tACL_CONN* p = btm_bda_to_acl(bd_addr, transport); |
| VLOG(1) << __func__ << " BDA: " << bd_addr; |
| |
| BTM_TRACE_DEBUG("btm_sec_bond_by_transport: Transport used %d", transport); |
| |
| /* Other security process is in progress */ |
| if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { |
| BTM_TRACE_ERROR("BTM_SecBond: already busy in state: %s", |
| btm_pair_state_descr(btm_cb.pairing_state)); |
| return (BTM_WRONG_MODE); |
| } |
| |
| p_dev_rec = btm_find_or_alloc_dev(bd_addr); |
| if (p_dev_rec == NULL) { |
| return (BTM_NO_RESOURCES); |
| } |
| |
| if (!controller_get_interface()->get_is_ready()) { |
| BTM_TRACE_ERROR("%s controller module is not ready", __func__); |
| return (BTM_NO_RESOURCES); |
| } |
| |
| BTM_TRACE_DEBUG("before update sec_flags=0x%x", p_dev_rec->sec_flags); |
| |
| /* Finished if connection is active and already paired */ |
| if (((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && |
| transport == BT_TRANSPORT_BR_EDR && |
| (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) || |
| ((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && |
| transport == BT_TRANSPORT_LE && |
| (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))) { |
| BTM_TRACE_WARNING("BTM_SecBond -> Already Paired"); |
| return (BTM_SUCCESS); |
| } |
| |
| /* Tell controller to get rid of the link key if it has one stored */ |
| if ((BTM_DeleteStoredLinkKey(&bd_addr, NULL)) != BTM_SUCCESS) |
| return (BTM_NO_RESOURCES); |
| |
| /* Save the PIN code if we got a valid one */ |
| if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) { |
| btm_cb.pin_code_len = pin_len; |
| p_dev_rec->pin_code_length = pin_len; |
| memcpy(btm_cb.pin_code, p_pin, PIN_CODE_LEN); |
| } |
| |
| btm_cb.pairing_bda = bd_addr; |
| |
| btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD; |
| |
| p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE; |
| p_dev_rec->is_originator = true; |
| if (trusted_mask) |
| BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); |
| |
| if (transport == BT_TRANSPORT_LE) { |
| btm_ble_init_pseudo_addr(p_dev_rec, bd_addr); |
| p_dev_rec->sec_flags &= ~BTM_SEC_LE_MASK; |
| |
| if (SMP_Pair(bd_addr) == SMP_STARTED) { |
| btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE; |
| p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); |
| return BTM_CMD_STARTED; |
| } |
| |
| btm_cb.pairing_flags = 0; |
| return (BTM_NO_RESOURCES); |
| } |
| |
| p_dev_rec->sec_flags &= |
| ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | |
| BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED); |
| |
| BTM_TRACE_DEBUG("after update sec_flags=0x%x", p_dev_rec->sec_flags); |
| if (!controller_get_interface()->supports_simple_pairing()) { |
| /* The special case when we authenticate keyboard. Set pin type to fixed */ |
| /* It would be probably better to do it from the application, but it is */ |
| /* complicated */ |
| if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == |
| BTM_COD_MAJOR_PERIPHERAL) && |
| (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) && |
| (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) { |
| btm_cb.pin_type_changed = true; |
| btsnd_hcic_write_pin_type(HCI_PIN_TYPE_FIXED); |
| } |
| } |
| |
| for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++) { |
| p_features = p_dev_rec->feature_pages[ii]; |
| BTM_TRACE_EVENT(" remote_features page[%1d] = %02x-%02x-%02x-%02x", ii, |
| p_features[0], p_features[1], p_features[2], p_features[3]); |
| BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x", |
| p_features[4], p_features[5], p_features[6], p_features[7]); |
| } |
| |
| BTM_TRACE_EVENT("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x", |
| p_dev_rec->sm4, p_dev_rec->hci_handle); |
| |
| #if (BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE) |
| p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN; |
| #endif |
| |
| /* If connection already exists... */ |
| if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE) { |
| btm_sec_start_authentication(p_dev_rec); |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ); |
| |
| /* Mark lcb as bonding */ |
| l2cu_update_lcb_4_bonding(bd_addr, true); |
| return (BTM_CMD_STARTED); |
| } |
| |
| BTM_TRACE_DEBUG("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4); |
| if (!controller_get_interface()->supports_simple_pairing() || |
| (p_dev_rec->sm4 == BTM_SM4_KNOWN)) { |
| if (btm_sec_check_prefetch_pin(p_dev_rec)) return (BTM_CMD_STARTED); |
| } |
| if ((btm_cb.security_mode == BTM_SEC_MODE_SP || |
| btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || |
| btm_cb.security_mode == BTM_SEC_MODE_SC) && |
| BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { |
| /* local is 2.1 and peer is unknown */ |
| if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) { |
| /* we are not accepting connection request from peer |
| * -> RNR (to learn if peer is 2.1) |
| * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME); |
| status = BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR); |
| } else { |
| /* We are accepting connection request from peer */ |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ); |
| status = BTM_CMD_STARTED; |
| } |
| BTM_TRACE_DEBUG("State:%s sm4: 0x%x sec_state:%d", |
| btm_pair_state_descr(btm_cb.pairing_state), p_dev_rec->sm4, |
| p_dev_rec->sec_state); |
| } else { |
| /* both local and peer are 2.1 */ |
| status = btm_sec_dd_create_conn(p_dev_rec); |
| } |
| |
| if (status != BTM_CMD_STARTED) { |
| BTM_TRACE_ERROR( |
| "%s BTM_ReadRemoteDeviceName or btm_sec_dd_create_conn error: 0x%x", |
| __func__, (int)status); |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| } |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecBondByTransport |
| * |
| * Description This function is called to perform bonding with peer device. |
| * If the connection is already up, but not secure, pairing |
| * is attempted. If already paired BTM_SUCCESS is returned. |
| * |
| * Parameters: bd_addr - Address of the device to bond |
| * transport - doing SSP over BR/EDR or SMP over LE |
| * pin_len - length in bytes of the PIN Code |
| * p_pin - pointer to array with the PIN Code |
| * trusted_mask - bitwise OR of trusted services |
| * (array of uint32_t) |
| * |
| * Note: After 2.1 parameters are not used and preserved here not to change API |
| ******************************************************************************/ |
| tBTM_STATUS BTM_SecBondByTransport(const RawAddress& bd_addr, |
| tBT_TRANSPORT transport, uint8_t pin_len, |
| uint8_t* p_pin, uint32_t trusted_mask[]) { |
| tBT_DEVICE_TYPE dev_type; |
| tBLE_ADDR_TYPE addr_type; |
| |
| BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); |
| /* LE device, do SMP pairing */ |
| if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) || |
| (transport == BT_TRANSPORT_BR_EDR && |
| (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) { |
| return BTM_ILLEGAL_ACTION; |
| } |
| return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, |
| trusted_mask); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecBond |
| * |
| * Description This function is called to perform bonding with peer device. |
| * If the connection is already up, but not secure, pairing |
| * is attempted. If already paired BTM_SUCCESS is returned. |
| * |
| * Parameters: bd_addr - Address of the device to bond |
| * pin_len - length in bytes of the PIN Code |
| * p_pin - pointer to array with the PIN Code |
| * trusted_mask - bitwise OR of trusted services |
| * (array of uint32_t) |
| * |
| * Note: After 2.1 parameters are not used and preserved here not to change API |
| ******************************************************************************/ |
| tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, uint8_t pin_len, |
| uint8_t* p_pin, uint32_t trusted_mask[]) { |
| tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; |
| if (BTM_UseLeLink(bd_addr)) transport = BT_TRANSPORT_LE; |
| return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, |
| trusted_mask); |
| } |
| /******************************************************************************* |
| * |
| * Function BTM_SecBondCancel |
| * |
| * Description This function is called to cancel ongoing bonding process |
| * with peer device. |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * transport - false for BR/EDR link; true for LE link |
| * |
| ******************************************************************************/ |
| tBTM_STATUS BTM_SecBondCancel(const RawAddress& bd_addr) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| BTM_TRACE_API("BTM_SecBondCancel() State: %s flags:0x%x", |
| btm_pair_state_descr(btm_cb.pairing_state), |
| btm_cb.pairing_flags); |
| p_dev_rec = btm_find_dev(bd_addr); |
| if (!p_dev_rec || btm_cb.pairing_bda != bd_addr) { |
| return BTM_UNKNOWN_ADDR; |
| } |
| |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_LE_ACTIVE) { |
| if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { |
| BTM_TRACE_DEBUG("Cancel LE pairing"); |
| if (SMP_PairCancel(bd_addr)) { |
| return BTM_CMD_STARTED; |
| } |
| } |
| return BTM_WRONG_MODE; |
| } |
| |
| BTM_TRACE_DEBUG("hci_handle:0x%x sec_state:%d", p_dev_rec->hci_handle, |
| p_dev_rec->sec_state); |
| if (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && |
| BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) { |
| /* pre-fetching pin for dedicated bonding */ |
| btm_sec_bond_cancel_complete(); |
| return BTM_SUCCESS; |
| } |
| |
| /* If this BDA is in a bonding procedure */ |
| if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && |
| (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) { |
| /* If the HCI link is up */ |
| if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) { |
| /* If some other thread disconnecting, we do not send second command */ |
| if ((p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING) || |
| (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH)) |
| return (BTM_CMD_STARTED); |
| |
| /* If the HCI link was set up by Bonding process */ |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) |
| return btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_PEER_USER, |
| p_dev_rec->hci_handle); |
| else |
| l2cu_update_lcb_4_bonding(bd_addr, false); |
| |
| return BTM_NOT_AUTHORIZED; |
| } else /*HCI link is not up */ |
| { |
| /* If the HCI link creation was started by Bonding process */ |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) { |
| btsnd_hcic_create_conn_cancel(bd_addr); |
| return BTM_CMD_STARTED; |
| } |
| if (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) { |
| BTM_CancelRemoteDeviceName(); |
| btm_cb.pairing_flags |= BTM_PAIR_FLAGS_WE_CANCEL_DD; |
| return BTM_CMD_STARTED; |
| } |
| return BTM_NOT_AUTHORIZED; |
| } |
| } |
| |
| return BTM_WRONG_MODE; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecGetDeviceLinkKey |
| * |
| * Description This function is called to obtain link key for the device |
| * it returns BTM_SUCCESS if link key is available, or |
| * BTM_UNKNOWN_ADDR if Security Manager does not know about |
| * the device or device record does not contain link key info |
| * |
| * Parameters: bd_addr - Address of the device |
| * link_key - Link Key is copied into this array |
| * |
| ******************************************************************************/ |
| tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr, |
| LINK_KEY link_key) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| p_dev_rec = btm_find_dev(bd_addr); |
| if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { |
| memcpy(link_key, p_dev_rec->link_key, LINK_KEY_LEN); |
| return (BTM_SUCCESS); |
| } |
| return (BTM_UNKNOWN_ADDR); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SecGetDeviceLinkKeyType |
| * |
| * Description This function is called to obtain link key type for the |
| * device. |
| * it returns BTM_SUCCESS if link key is available, or |
| * BTM_UNKNOWN_ADDR if Security Manager does not know about |
| * the device or device record does not contain link key info |
| * |
| * Returns BTM_LKEY_TYPE_IGNORE if link key is unknown, link type |
| * otherwise. |
| * |
| ******************************************************************************/ |
| tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType(const RawAddress& bd_addr) { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr); |
| |
| if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { |
| return p_dev_rec->link_key_type; |
| } |
| return BTM_LKEY_TYPE_IGNORE; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SetEncryption |
| * |
| * Description This function is called to ensure that connection is |
| * encrypted. Should be called only on an open connection. |
| * Typically only needed for connections that first want to |
| * bring up unencrypted links, then later encrypt them. |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * transport - Link transport |
| * p_callback - Pointer to callback function called if |
| * this function returns PENDING after required |
| * procedures are completed. Can be set to |
| * NULL if status is not desired. |
| * p_ref_data - pointer to any data the caller wishes to |
| * receive in the callback function upon |
| * completion. can be set to NULL if not used. |
| * sec_act - LE security action, unused for BR/EDR |
| * |
| * Returns BTM_SUCCESS - already encrypted |
| * BTM_PENDING - command will be returned in the callback |
| * BTM_WRONG_MODE- connection not up. |
| * BTM_BUSY - security procedures are currently active |
| * BTM_MODE_UNSUPPORTED - if security manager not linked in. |
| * |
| ******************************************************************************/ |
| tBTM_STATUS BTM_SetEncryption(const RawAddress& bd_addr, |
| tBT_TRANSPORT transport, |
| tBTM_SEC_CBACK* p_callback, void* p_ref_data, |
| tBTM_BLE_SEC_ACT sec_act) { |
| tBTM_STATUS rc = 0; |
| |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr); |
| if (!p_dev_rec || (transport == BT_TRANSPORT_BR_EDR && |
| p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) || |
| (transport == BT_TRANSPORT_LE && |
| p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE)) { |
| /* Connection should be up and runnning */ |
| BTM_TRACE_WARNING("Security Manager: BTM_SetEncryption not connected"); |
| |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_WRONG_MODE); |
| |
| return (BTM_WRONG_MODE); |
| } |
| |
| if (transport == BT_TRANSPORT_BR_EDR && |
| (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)) { |
| BTM_TRACE_EVENT("Security Manager: BTM_SetEncryption already encrypted"); |
| |
| if (*p_callback) |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_SUCCESS); |
| |
| return (BTM_SUCCESS); |
| } |
| |
| /* enqueue security request if security is active */ |
| if (p_dev_rec->p_callback || (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE)) { |
| BTM_TRACE_WARNING( |
| "Security Manager: BTM_SetEncryption busy, enqueue request"); |
| |
| if (btm_sec_queue_encrypt_request(bd_addr, transport, p_callback, |
| p_ref_data, sec_act)) { |
| return BTM_CMD_STARTED; |
| } else { |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_NO_RESOURCES); |
| return BTM_NO_RESOURCES; |
| } |
| } |
| |
| p_dev_rec->p_callback = p_callback; |
| p_dev_rec->p_ref_data = p_ref_data; |
| p_dev_rec->security_required |= |
| (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); |
| p_dev_rec->is_originator = false; |
| |
| BTM_TRACE_API( |
| "Security Manager: BTM_SetEncryption Handle:%d State:%d Flags:0x%x " |
| "Required:0x%x", |
| p_dev_rec->hci_handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, |
| p_dev_rec->security_required); |
| |
| if (transport == BT_TRANSPORT_LE) { |
| tACL_CONN* p = btm_bda_to_acl(bd_addr, transport); |
| if (p) { |
| rc = btm_ble_set_encryption(bd_addr, sec_act, p->link_role); |
| } else { |
| rc = BTM_WRONG_MODE; |
| BTM_TRACE_WARNING("%s: cannot call btm_ble_set_encryption, p is NULL", |
| __func__); |
| } |
| } else { |
| rc = btm_sec_execute_procedure(p_dev_rec); |
| } |
| |
| if (rc != BTM_CMD_STARTED && rc != BTM_BUSY) { |
| if (p_callback) { |
| p_dev_rec->p_callback = NULL; |
| (*p_callback)(&bd_addr, transport, p_dev_rec->p_ref_data, rc); |
| } |
| } |
| |
| return (rc); |
| } |
| |
| /******************************************************************************* |
| * disconnect the ACL link, if it's not done yet. |
| ******************************************************************************/ |
| static tBTM_STATUS btm_sec_send_hci_disconnect(tBTM_SEC_DEV_REC* p_dev_rec, |
| uint8_t reason, |
| uint16_t conn_handle) { |
| uint8_t old_state = p_dev_rec->sec_state; |
| tBTM_STATUS status = BTM_CMD_STARTED; |
| |
| BTM_TRACE_EVENT("btm_sec_send_hci_disconnect: handle:0x%x, reason=0x%x", |
| conn_handle, reason); |
| |
| /* send HCI_Disconnect on a transport only once */ |
| switch (old_state) { |
| case BTM_SEC_STATE_DISCONNECTING: |
| if (conn_handle == p_dev_rec->hci_handle) return status; |
| |
| p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH; |
| break; |
| |
| case BTM_SEC_STATE_DISCONNECTING_BLE: |
| if (conn_handle == p_dev_rec->ble_hci_handle) return status; |
| |
| p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH; |
| break; |
| |
| case BTM_SEC_STATE_DISCONNECTING_BOTH: |
| return status; |
| |
| default: |
| p_dev_rec->sec_state = (conn_handle == p_dev_rec->hci_handle) |
| ? BTM_SEC_STATE_DISCONNECTING |
| : BTM_SEC_STATE_DISCONNECTING_BLE; |
| |
| break; |
| } |
| |
| /* If a role switch is in progress, delay the HCI Disconnect to avoid |
| * controller problem */ |
| if (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING && |
| p_dev_rec->hci_handle == conn_handle) { |
| BTM_TRACE_DEBUG( |
| "RS in progress - Set DISC Pending flag in btm_sec_send_hci_disconnect " |
| "to delay disconnect"); |
| p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; |
| status = BTM_SUCCESS; |
| } |
| /* Tear down the HCI link */ |
| else { |
| btsnd_hcic_disconnect(conn_handle, reason); |
| } |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_ConfirmReqReply |
| * |
| * Description This function is called to confirm the numeric value for |
| * Simple Pairing in response to BTM_SP_CFM_REQ_EVT |
| * |
| * Parameters: res - result of the operation BTM_SUCCESS if |
| * success |
| * bd_addr - Address of the peer device |
| * |
| ******************************************************************************/ |
| void BTM_ConfirmReqReply(tBTM_STATUS res, const RawAddress& bd_addr) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| BTM_TRACE_EVENT("BTM_ConfirmReqReply() State: %s Res: %u", |
| btm_pair_state_descr(btm_cb.pairing_state), res); |
| |
| /* If timeout already expired or has been canceled, ignore the reply */ |
| if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM) || |
| (btm_cb.pairing_bda != bd_addr)) |
| return; |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); |
| |
| if ((res == BTM_SUCCESS) || (res == BTM_SUCCESS_NO_SECURITY)) { |
| btm_cb.acl_disc_reason = HCI_SUCCESS; |
| |
| if (res == BTM_SUCCESS) { |
| p_dev_rec = btm_find_dev(bd_addr); |
| if (p_dev_rec != NULL) { |
| p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; |
| p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; |
| } |
| } |
| |
| btsnd_hcic_user_conf_reply(bd_addr, true); |
| } else { |
| /* Report authentication failed event from state |
| * BTM_PAIR_STATE_WAIT_AUTH_COMPLETE */ |
| btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; |
| btsnd_hcic_user_conf_reply(bd_addr, false); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_PasskeyReqReply |
| * |
| * Description This function is called to provide the passkey for |
| * Simple Pairing in response to BTM_SP_KEY_REQ_EVT |
| * |
| * Parameters: res - result of the operation BTM_SUCCESS if success |
| * bd_addr - Address of the peer device |
| * passkey - numeric value in the range of |
| * BTM_MIN_PASSKEY_VAL(0) - |
| * BTM_MAX_PASSKEY_VAL(999999(0xF423F)). |
| * |
| ******************************************************************************/ |
| #if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) |
| void BTM_PasskeyReqReply(tBTM_STATUS res, const RawAddress& bd_addr, |
| uint32_t passkey) { |
| BTM_TRACE_API("BTM_PasskeyReqReply: State: %s res:%d", |
| btm_pair_state_descr(btm_cb.pairing_state), res); |
| |
| if ((btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) || |
| (btm_cb.pairing_bda != bd_addr)) { |
| return; |
| } |
| |
| /* If timeout already expired or has been canceled, ignore the reply */ |
| if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) && |
| (res != BTM_SUCCESS)) { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr); |
| if (p_dev_rec != NULL) { |
| btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; |
| |
| if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) |
| btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_AUTH_FAILURE, |
| p_dev_rec->hci_handle); |
| else |
| BTM_SecBondCancel(bd_addr); |
| |
| p_dev_rec->sec_flags &= |
| ~(BTM_SEC_LINK_KEY_AUTHED | BTM_SEC_LINK_KEY_KNOWN); |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| return; |
| } |
| } else if (btm_cb.pairing_state != BTM_PAIR_STATE_KEY_ENTRY) |
| return; |
| |
| if (passkey > BTM_MAX_PASSKEY_VAL) res = BTM_ILLEGAL_VALUE; |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); |
| |
| if (res != BTM_SUCCESS) { |
| /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed |
| * event */ |
| btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; |
| btsnd_hcic_user_passkey_neg_reply(bd_addr); |
| } else { |
| btm_cb.acl_disc_reason = HCI_SUCCESS; |
| btsnd_hcic_user_passkey_reply(bd_addr, passkey); |
| } |
| } |
| #endif |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SendKeypressNotif |
| * |
| * Description This function is used during the passkey entry model |
| * by a device with KeyboardOnly IO capabilities |
| * (very likely to be a HID Device). |
| * It is called by a HID Device to inform the remote device |
| * when a key has been entered or erased. |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * type - notification type |
| * |
| ******************************************************************************/ |
| #if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) |
| void BTM_SendKeypressNotif(const RawAddress& bd_addr, tBTM_SP_KEY_TYPE type) { |
| /* This API only make sense between PASSKEY_REQ and SP complete */ |
| if (btm_cb.pairing_state == BTM_PAIR_STATE_KEY_ENTRY) |
| btsnd_hcic_send_keypress_notif(bd_addr, type); |
| } |
| #endif |
| |
| /******************************************************************************* |
| * |
| * Function BTM_IoCapRsp |
| * |
| * Description This function is called in response to BTM_SP_IO_REQ_EVT |
| * When the event data io_req.oob_data is set to |
| * BTM_OOB_UNKNOWN by the tBTM_SP_CALLBACK implementation, |
| * this function is called to provide the actual response |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * io_cap - The IO capability of local device. |
| * oob - BTM_OOB_NONE or BTM_OOB_PRESENT. |
| * auth_req- MITM protection required or not. |
| * |
| ******************************************************************************/ |
| void BTM_IoCapRsp(const RawAddress& bd_addr, tBTM_IO_CAP io_cap, |
| tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req) { |
| BTM_TRACE_EVENT("BTM_IoCapRsp: state: %s oob: %d io_cap: %d", |
| btm_pair_state_descr(btm_cb.pairing_state), oob, io_cap); |
| |
| if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS) || |
| (btm_cb.pairing_bda != bd_addr)) |
| return; |
| |
| if (oob < BTM_OOB_UNKNOWN && io_cap < BTM_IO_CAP_MAX) { |
| btm_cb.devcb.loc_auth_req = auth_req; |
| btm_cb.devcb.loc_io_caps = io_cap; |
| |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) |
| auth_req = (BTM_AUTH_DD_BOND | (auth_req & BTM_AUTH_YN_BIT)); |
| |
| btsnd_hcic_io_cap_req_reply(bd_addr, io_cap, oob, auth_req); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_ReadLocalOobData |
| * |
| * Description This function is called to read the local OOB data from |
| * LM |
| * |
| ******************************************************************************/ |
| void BTM_ReadLocalOobData(void) { btsnd_hcic_read_local_oob_data(); } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_RemoteOobDataReply |
| * |
| * Description This function is called to provide the remote OOB data for |
| * Simple Pairing in response to BTM_SP_RMT_OOB_EVT |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * c - simple pairing Hash C. |
| * r - simple pairing Randomizer C. |
| * |
| ******************************************************************************/ |
| void BTM_RemoteOobDataReply(tBTM_STATUS res, const RawAddress& bd_addr, |
| BT_OCTET16 c, BT_OCTET16 r) { |
| BTM_TRACE_EVENT("%s() - State: %s res: %d", __func__, |
| btm_pair_state_descr(btm_cb.pairing_state), res); |
| |
| /* If timeout already expired or has been canceled, ignore the reply */ |
| if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP) return; |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); |
| |
| if (res != BTM_SUCCESS) { |
| /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed |
| * event */ |
| btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; |
| btsnd_hcic_rem_oob_neg_reply(bd_addr); |
| } else { |
| btm_cb.acl_disc_reason = HCI_SUCCESS; |
| btsnd_hcic_rem_oob_reply(bd_addr, c, r); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_BuildOobData |
| * |
| * Description This function is called to build the OOB data payload to |
| * be sent over OOB (non-Bluetooth) link |
| * |
| * Parameters: p_data - the location for OOB data |
| * max_len - p_data size. |
| * c - simple pairing Hash C. |
| * r - simple pairing Randomizer C. |
| * name_len- 0, local device name would not be included. |
| * otherwise, the local device name is included for |
| * up to this specified length |
| * |
| * Returns Number of bytes in p_data. |
| * |
| ******************************************************************************/ |
| uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, BT_OCTET16 c, |
| BT_OCTET16 r, uint8_t name_len) { |
| uint8_t* p = p_data; |
| uint16_t len = 0; |
| uint16_t name_size; |
| uint8_t name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE; |
| |
| if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) { |
| /* add mandatory part */ |
| UINT16_TO_STREAM(p, len); |
| BDADDR_TO_STREAM(p, *controller_get_interface()->get_address()); |
| |
| len = BTM_OOB_MANDATORY_SIZE; |
| max_len -= len; |
| |
| /* now optional part */ |
| |
| /* add Hash C */ |
| uint16_t delta = BTM_OOB_HASH_C_SIZE + 2; |
| if (max_len >= delta) { |
| *p++ = BTM_OOB_HASH_C_SIZE + 1; |
| *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE; |
| ARRAY_TO_STREAM(p, c, BTM_OOB_HASH_C_SIZE); |
| len += delta; |
| max_len -= delta; |
| } |
| |
| /* add Rand R */ |
| delta = BTM_OOB_RAND_R_SIZE + 2; |
| if (max_len >= delta) { |
| *p++ = BTM_OOB_RAND_R_SIZE + 1; |
| *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE; |
| ARRAY_TO_STREAM(p, r, BTM_OOB_RAND_R_SIZE); |
| len += delta; |
| max_len -= delta; |
| } |
| |
| /* add class of device */ |
| delta = BTM_OOB_COD_SIZE + 2; |
| if (max_len >= delta) { |
| *p++ = BTM_OOB_COD_SIZE + 1; |
| *p++ = BTM_EIR_OOB_COD_TYPE; |
| DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class); |
| len += delta; |
| max_len -= delta; |
| } |
| name_size = name_len; |
| if (name_size > strlen(btm_cb.cfg.bd_name)) { |
| name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE; |
| name_size = (uint16_t)strlen(btm_cb.cfg.bd_name); |
| } |
| delta = name_size + 2; |
| if (max_len >= delta) { |
| *p++ = name_size + 1; |
| *p++ = name_type; |
| ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, name_size); |
| len += delta; |
| max_len -= delta; |
| } |
| /* update len */ |
| p = p_data; |
| UINT16_TO_STREAM(p, len); |
| } |
| return len; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_BothEndsSupportSecureConnections |
| * |
| * Description This function is called to check if both the local device |
| * and the peer device specified by bd_addr support BR/EDR |
| * Secure Connections. |
| * |
| * Parameters: bd_addr - address of the peer |
| * |
| * Returns true if BR/EDR Secure Connections are supported by both |
| * local and the remote device, else false. |
| * |
| ******************************************************************************/ |
| bool BTM_BothEndsSupportSecureConnections(const RawAddress& bd_addr) { |
| return ((controller_get_interface()->supports_secure_connections()) && |
| (BTM_PeerSupportsSecureConnections(bd_addr))); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_PeerSupportsSecureConnections |
| * |
| * Description This function is called to check if the peer supports |
| * BR/EDR Secure Connections. |
| * |
| * Parameters: bd_addr - address of the peer |
| * |
| * Returns true if BR/EDR Secure Connections are supported by the peer, |
| * else false. |
| * |
| ******************************************************************************/ |
| bool BTM_PeerSupportsSecureConnections(const RawAddress& bd_addr) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| p_dev_rec = btm_find_dev(bd_addr); |
| if (p_dev_rec == NULL) { |
| LOG(WARNING) << __func__ << ": unknown BDA: " << bd_addr; |
| return false; |
| } |
| |
| return (p_dev_rec->remote_supports_secure_connections); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_ReadOobData |
| * |
| * Description This function is called to parse the OOB data payload |
| * received over OOB (non-Bluetooth) link |
| * |
| * Parameters: p_data - the location for OOB data |
| * eir_tag - The associated EIR tag to read the data. |
| * *p_len(output) - the length of the data with the given tag. |
| * |
| * Returns the beginning of the data with the given tag. |
| * NULL, if the tag is not found. |
| * |
| ******************************************************************************/ |
| uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag, uint8_t* p_len) { |
| uint8_t* p = p_data; |
| uint16_t max_len; |
| uint8_t len, type; |
| uint8_t* p_ret = NULL; |
| uint8_t ret_len = 0; |
| |
| if (p_data) { |
| STREAM_TO_UINT16(max_len, p); |
| if (max_len >= BTM_OOB_MANDATORY_SIZE) { |
| if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) { |
| p_ret = p; /* the location for bd_addr */ |
| ret_len = BTM_OOB_BD_ADDR_SIZE; |
| } else { |
| p += BD_ADDR_LEN; |
| max_len -= BTM_OOB_MANDATORY_SIZE; |
| /* now the optional data in EIR format */ |
| while (max_len > 0) { |
| len = *p++; /* tag data len + 1 */ |
| type = *p++; |
| if (eir_tag == type) { |
| p_ret = p; |
| ret_len = len - 1; |
| break; |
| } |
| /* the data size of this tag is len + 1 (tag data len + 2) */ |
| if (max_len > len) { |
| max_len -= len; |
| max_len--; |
| len--; |
| p += len; |
| } else |
| max_len = 0; |
| } |
| } |
| } |
| } |
| |
| if (p_len) *p_len = ret_len; |
| |
| return p_ret; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function BTM_SetOutService |
| * |
| * Description This function is called to set the service for |
| * outgoing connections. |
| * |
| * If the profile/application calls BTM_SetSecurityLevel |
| * before initiating a connection, this function does not |
| * need to be called. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void BTM_SetOutService(const RawAddress& bd_addr, uint8_t service_id, |
| uint32_t mx_chan_id) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| tBTM_SEC_SERV_REC* p_serv_rec = &btm_cb.sec_serv_rec[0]; |
| |
| btm_cb.p_out_serv = p_serv_rec; |
| p_dev_rec = btm_find_dev(bd_addr); |
| |
| for (int i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) { |
| if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) && |
| (p_serv_rec->service_id == service_id) && |
| (p_serv_rec->orig_mx_chan_id == mx_chan_id)) { |
| BTM_TRACE_API( |
| "BTM_SetOutService p_out_serv id %d, psm 0x%04x, proto_id %d, " |
| "chan_id %d", |
| p_serv_rec->service_id, p_serv_rec->psm, p_serv_rec->mx_proto_id, |
| p_serv_rec->orig_mx_chan_id); |
| btm_cb.p_out_serv = p_serv_rec; |
| if (p_dev_rec) p_dev_rec->p_cur_service = p_serv_rec; |
| break; |
| } |
| } |
| } |
| |
| /************************************************************************ |
| * I N T E R N A L F U N C T I O N S |
| ************************************************************************/ |
| /******************************************************************************* |
| * |
| * Function btm_sec_is_upgrade_possible |
| * |
| * Description This function returns true if the existing link key |
| * can be upgraded or if the link key does not exist. |
| * |
| * Returns bool |
| * |
| ******************************************************************************/ |
| static bool btm_sec_is_upgrade_possible(tBTM_SEC_DEV_REC* p_dev_rec, |
| bool is_originator) { |
| uint16_t mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM; |
| bool is_possible = true; |
| |
| if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) { |
| is_possible = false; |
| if (p_dev_rec->p_cur_service) { |
| BTM_TRACE_DEBUG( |
| "%s() id: %d, link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x, " |
| "flags: 0x%x", |
| __func__, p_dev_rec->p_cur_service->service_id, |
| p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, mtm_check, |
| p_dev_rec->p_cur_service->security_flags); |
| } else { |
| BTM_TRACE_DEBUG( |
| "%s() link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x", __func__, |
| p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, mtm_check); |
| } |
| /* Already have a link key to the connected peer. Is the link key secure |
| *enough? |
| ** Is a link key upgrade even possible? |
| */ |
| if ((p_dev_rec->security_required & mtm_check) /* needs MITM */ |
| && ((p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB) || |
| (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256)) |
| /* has unauthenticated |
| link key */ |
| && (p_dev_rec->rmt_io_caps < BTM_IO_CAP_MAX) /* a valid peer IO cap */ |
| && (btm_sec_io_map[p_dev_rec->rmt_io_caps][btm_cb.devcb.loc_io_caps])) |
| /* authenticated |
| link key is possible */ |
| { |
| /* upgrade is possible: check if the application wants the upgrade. |
| * If the application is configured to use a global MITM flag, |
| * it probably would not want to upgrade the link key based on the |
| * security level database */ |
| is_possible = true; |
| } |
| } |
| BTM_TRACE_DEBUG("%s() is_possible: %d sec_flags: 0x%x", __func__, is_possible, |
| p_dev_rec->sec_flags); |
| return is_possible; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_check_upgrade |
| * |
| * Description This function is called to check if the existing link key |
| * needs to be upgraded. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC* p_dev_rec, |
| bool is_originator) { |
| BTM_TRACE_DEBUG("%s()", __func__); |
| |
| /* Only check if link key already exists */ |
| if (!(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) return; |
| |
| if (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == true) { |
| BTM_TRACE_DEBUG("need upgrade!! sec_flags:0x%x", p_dev_rec->sec_flags); |
| /* upgrade is possible: check if the application wants the upgrade. |
| * If the application is configured to use a global MITM flag, |
| * it probably would not want to upgrade the link key based on the security |
| * level database */ |
| tBTM_SP_UPGRADE evt_data; |
| evt_data.bd_addr = p_dev_rec->bd_addr; |
| evt_data.upgrade = true; |
| if (btm_cb.api.p_sp_callback) |
| (*btm_cb.api.p_sp_callback)(BTM_SP_UPGRADE_EVT, |
| (tBTM_SP_EVT_DATA*)&evt_data); |
| |
| BTM_TRACE_DEBUG("evt_data.upgrade:0x%x", evt_data.upgrade); |
| if (evt_data.upgrade) { |
| /* if the application confirms the upgrade, set the upgrade bit */ |
| p_dev_rec->sm4 |= BTM_SM4_UPGRADE; |
| |
| /* Clear the link key known to go through authentication/pairing again */ |
| p_dev_rec->sec_flags &= |
| ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED); |
| p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED; |
| BTM_TRACE_DEBUG("sec_flags:0x%x", p_dev_rec->sec_flags); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_l2cap_access_req |
| * |
| * Description This function is called by the L2CAP to grant permission to |
| * establish L2CAP connection to or from the peer device. |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * psm - L2CAP PSM |
| * is_originator - true if protocol above L2CAP originates |
| * connection |
| * p_callback - Pointer to callback function called if |
| * this function returns PENDING after required |
| * procedures are complete. MUST NOT BE NULL. |
| * |
| * Returns tBTM_STATUS |
| * |
| ******************************************************************************/ |
| tBTM_STATUS btm_sec_l2cap_access_req(const RawAddress& bd_addr, uint16_t psm, |
| uint16_t handle, CONNECTION_TYPE conn_type, |
| tBTM_SEC_CALLBACK* p_callback, |
| void* p_ref_data) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| tBTM_SEC_SERV_REC* p_serv_rec; |
| uint16_t security_required; |
| uint16_t old_security_required; |
| bool old_is_originator; |
| tBTM_STATUS rc = BTM_SUCCESS; |
| bool chk_acp_auth_done = false; |
| bool is_originator; |
| tBT_TRANSPORT transport = |
| BT_TRANSPORT_BR_EDR; /* should check PSM range in LE connection oriented |
| L2CAP connection */ |
| |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (conn_type & CONNECTION_TYPE_ORIG_MASK) |
| is_originator = true; |
| else |
| is_originator = false; |
| |
| BTM_TRACE_DEBUG("%s() conn_type: 0x%x, 0x%x", __func__, conn_type, |
| p_ref_data); |
| #else |
| is_originator = conn_type; |
| |
| BTM_TRACE_DEBUG("%s() is_originator:%d, 0x%x", __func__, is_originator, |
| p_ref_data); |
| #endif |
| |
| /* Find or get oldest record */ |
| p_dev_rec = btm_find_or_alloc_dev(bd_addr); |
| |
| p_dev_rec->hci_handle = handle; |
| |
| /* Find the service record for the PSM */ |
| p_serv_rec = btm_sec_find_first_serv(conn_type, psm); |
| |
| /* If there is no application registered with this PSM do not allow connection |
| */ |
| if (!p_serv_rec) { |
| BTM_TRACE_WARNING("%s() PSM: %d no application registerd", __func__, psm); |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED); |
| return (BTM_MODE_UNSUPPORTED); |
| } |
| |
| /* Services level0 by default have no security */ |
| if ((btm_sec_is_serv_level0(psm)) && |
| (!btm_cb.devcb.secure_connections_only)) { |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_SUCCESS_NO_SECURITY); |
| return (BTM_SUCCESS); |
| } |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (conn_type & CONNECTION_TYPE_CONNLESS_MASK) { |
| if (btm_cb.security_mode == BTM_SEC_MODE_SC) { |
| security_required = btm_sec_set_serv_level4_flags( |
| p_serv_rec->ucd_security_flags, is_originator); |
| } else { |
| security_required = p_serv_rec->ucd_security_flags; |
| } |
| |
| rc = BTM_CMD_STARTED; |
| if (is_originator) { |
| if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == |
| BTM_SEC_OUT_AUTHENTICATE) && |
| (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == |
| (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && |
| (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && |
| (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED)))) { |
| rc = BTM_SUCCESS; |
| } |
| } else { |
| if (((security_required & BTM_SEC_IN_FLAGS) == 0) || |
| ((((security_required & BTM_SEC_IN_FLAGS) == |
| BTM_SEC_IN_AUTHENTICATE) && |
| (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || |
| ((((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && |
| (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || |
| ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && |
| (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED)))) { |
| // Check for 16 digits (or MITM) |
| if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || |
| (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == |
| BTM_SEC_IN_MIN_16_DIGIT_PIN) && |
| btm_dev_16_digit_authenticated(p_dev_rec))) { |
| rc = BTM_SUCCESS; |
| } |
| } |
| } |
| |
| if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && |
| (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { |
| rc = BTM_CMD_STARTED; |
| } |
| |
| if (rc == BTM_SUCCESS) { |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, (void*)p_ref_data, BTM_SUCCESS); |
| |
| return (BTM_SUCCESS); |
| } |
| } else |
| #endif |
| { |
| if (btm_cb.security_mode == BTM_SEC_MODE_SC) { |
| security_required = btm_sec_set_serv_level4_flags( |
| p_serv_rec->security_flags, is_originator); |
| } else { |
| security_required = p_serv_rec->security_flags; |
| } |
| } |
| |
| BTM_TRACE_DEBUG( |
| "%s: security_required 0x%04x, is_originator 0x%02x, psm 0x%04x", |
| __func__, security_required, is_originator, psm); |
| |
| if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4)) { |
| bool local_supports_sc = |
| controller_get_interface()->supports_secure_connections(); |
| /* acceptor receives L2CAP Channel Connect Request for Secure Connections |
| * Only service */ |
| if (!(local_supports_sc) || |
| !(p_dev_rec->remote_supports_secure_connections)) { |
| BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d", |
| "rmt_support_for_sc : %d -> fail pairing", __func__, |
| local_supports_sc, |
| p_dev_rec->remote_supports_secure_connections); |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, (void*)p_ref_data, |
| BTM_MODE4_LEVEL4_NOT_SUPPORTED); |
| |
| return (BTM_MODE4_LEVEL4_NOT_SUPPORTED); |
| } |
| } |
| |
| /* there are some devices (moto KRZR) which connects to several services at |
| * the same time */ |
| /* we will process one after another */ |
| if ((p_dev_rec->p_callback) || |
| (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)) { |
| BTM_TRACE_EVENT("%s() - busy - PSM:%d delayed state: %s mode:%d, sm4:0x%x", |
| __func__, psm, btm_pair_state_descr(btm_cb.pairing_state), |
| btm_cb.security_mode, p_dev_rec->sm4); |
| BTM_TRACE_EVENT("security_flags:x%x, sec_flags:x%x", security_required, |
| p_dev_rec->sec_flags); |
| rc = BTM_CMD_STARTED; |
| if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || |
| btm_cb.security_mode == BTM_SEC_MODE_NONE || |
| btm_cb.security_mode == BTM_SEC_MODE_SERVICE || |
| btm_cb.security_mode == BTM_SEC_MODE_LINK) || |
| (BTM_SM4_KNOWN == p_dev_rec->sm4) || |
| (BTM_SEC_IS_SM4(p_dev_rec->sm4) && |
| (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == false))) { |
| /* legacy mode - local is legacy or local is lisbon/peer is legacy |
| * or SM4 with no possibility of link key upgrade */ |
| if (is_originator) { |
| if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == |
| BTM_SEC_OUT_AUTHENTICATE) && |
| btm_dev_authenticated(p_dev_rec))) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == |
| (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && |
| btm_dev_encrypted(p_dev_rec))) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && |
| btm_dev_authorized(p_dev_rec) && btm_dev_encrypted(p_dev_rec)))) { |
| rc = BTM_SUCCESS; |
| } |
| } else { |
| if (((security_required & BTM_SEC_IN_FLAGS) == 0) || |
| (((security_required & BTM_SEC_IN_FLAGS) == |
| BTM_SEC_IN_AUTHENTICATE) && |
| btm_dev_authenticated(p_dev_rec)) || |
| (((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && |
| btm_dev_encrypted(p_dev_rec)) || |
| (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) && |
| (btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec))) || |
| (((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_AUTHORIZE)) && |
| ((btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec)) && |
| btm_dev_authenticated(p_dev_rec))) || |
| (((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE)) && |
| ((btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec)) && |
| btm_dev_encrypted(p_dev_rec))) || |
| (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && |
| btm_dev_encrypted(p_dev_rec) && |
| (btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec)))) { |
| // Check for 16 digits (or MITM) |
| if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || |
| (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == |
| BTM_SEC_IN_MIN_16_DIGIT_PIN) && |
| btm_dev_16_digit_authenticated(p_dev_rec))) { |
| rc = BTM_SUCCESS; |
| } |
| } |
| } |
| |
| if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && |
| (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { |
| rc = BTM_CMD_STARTED; |
| } |
| |
| if (rc == BTM_SUCCESS) { |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, (void*)p_ref_data, BTM_SUCCESS); |
| return (BTM_SUCCESS); |
| } |
| } |
| |
| btm_cb.sec_req_pending = true; |
| return (BTM_CMD_STARTED); |
| } |
| |
| /* Save pointer to service record */ |
| p_dev_rec->p_cur_service = p_serv_rec; |
| |
| /* Modify security_required in btm_sec_l2cap_access_req for Lisbon */ |
| if (btm_cb.security_mode == BTM_SEC_MODE_SP || |
| btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || |
| btm_cb.security_mode == BTM_SEC_MODE_SC) { |
| if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { |
| if (is_originator) { |
| /* SM4 to SM4 -> always authenticate & encrypt */ |
| security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); |
| } else /* acceptor */ |
| { |
| /* SM4 to SM4: the acceptor needs to make sure the authentication is |
| * already done */ |
| chk_acp_auth_done = true; |
| /* SM4 to SM4 -> always authenticate & encrypt */ |
| security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); |
| } |
| } else if (!(BTM_SM4_KNOWN & p_dev_rec->sm4)) { |
| /* the remote features are not known yet */ |
| BTM_TRACE_DEBUG("%s: (%s) remote features unknown!!sec_flags:0x%02x", |
| __func__, (is_originator) ? "initiator" : "acceptor", |
| p_dev_rec->sec_flags); |
| |
| p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; |
| return (BTM_CMD_STARTED); |
| } |
| } |
| |
| BTM_TRACE_DEBUG( |
| "%s() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d", __func__, |
| p_dev_rec->sm4, p_dev_rec->sec_flags, security_required, |
| chk_acp_auth_done); |
| |
| old_security_required = p_dev_rec->security_required; |
| old_is_originator = p_dev_rec->is_originator; |
| p_dev_rec->security_required = security_required; |
| p_dev_rec->p_ref_data = p_ref_data; |
| p_dev_rec->is_originator = is_originator; |
| |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (conn_type & CONNECTION_TYPE_CONNLESS_MASK) |
| p_dev_rec->is_ucd = true; |
| else |
| p_dev_rec->is_ucd = false; |
| #endif |
| |
| /* If there are multiple service records used through the same PSM */ |
| /* leave security decision for the multiplexor on the top */ |
| #if (L2CAP_UCD_INCLUDED == TRUE) |
| if (((btm_sec_find_next_serv(p_serv_rec)) != NULL) && |
| (!(conn_type & CONNECTION_TYPE_CONNLESS_MASK))) /* if not UCD */ |
| #else |
| if ((btm_sec_find_next_serv(p_serv_rec)) != NULL) |
| #endif |
| { |
| BTM_TRACE_DEBUG("no next_serv sm4:0x%x, chk:%d", p_dev_rec->sm4, |
| chk_acp_auth_done); |
| if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) { |
| BTM_TRACE_EVENT( |
| "Security Manager: l2cap_access_req PSM:%d postponed for multiplexer", |
| psm); |
| /* pre-Lisbon: restore the old settings */ |
| p_dev_rec->security_required = old_security_required; |
| p_dev_rec->is_originator = old_is_originator; |
| |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_SUCCESS); |
| |
| return (BTM_SUCCESS); |
| } |
| } |
| |
| /* if the originator is using dynamic PSM in legacy mode, do not start any |
| * security process now |
| * The layer above L2CAP needs to carry out the security requirement after |
| * L2CAP connect |
| * response is received */ |
| if (is_originator && ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || |
| btm_cb.security_mode == BTM_SEC_MODE_NONE || |
| btm_cb.security_mode == BTM_SEC_MODE_SERVICE || |
| btm_cb.security_mode == BTM_SEC_MODE_LINK) || |
| !BTM_SEC_IS_SM4(p_dev_rec->sm4)) && |
| (psm >= 0x1001)) { |
| BTM_TRACE_EVENT( |
| "dynamic PSM:0x%x in legacy mode - postponed for upper layer", psm); |
| /* restore the old settings */ |
| p_dev_rec->security_required = old_security_required; |
| p_dev_rec->is_originator = old_is_originator; |
| |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_SUCCESS); |
| |
| return (BTM_SUCCESS); |
| } |
| |
| if (chk_acp_auth_done) { |
| BTM_TRACE_DEBUG( |
| "(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: " |
| "x%x", |
| (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED), |
| (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)); |
| /* SM4, but we do not know for sure which level of security we need. |
| * as long as we have a link key, it's OK */ |
| if ((0 == (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) || |
| (0 == (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) { |
| rc = BTM_DELAY_CHECK; |
| /* |
| 2046 may report HCI_Encryption_Change and L2C Connection Request out of |
| sequence |
| because of data path issues. Delay this disconnect a little bit |
| */ |
| LOG_INFO( |
| LOG_TAG, |
| "%s peer should have initiated security process by now (SM4 to SM4)", |
| __func__); |
| p_dev_rec->p_callback = p_callback; |
| p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC; |
| (*p_callback)(&bd_addr, transport, p_ref_data, rc); |
| |
| return BTM_SUCCESS; |
| } |
| } |
| |
| p_dev_rec->p_callback = p_callback; |
| |
| if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID || |
| p_dev_rec->last_author_service_id != |
| p_dev_rec->p_cur_service->service_id) { |
| /* Although authentication and encryption are per connection |
| ** authorization is per access request. For example when serial connection |
| ** is up and authorized and client requests to read file (access to other |
| ** scn), we need to request user's permission again. |
| */ |
| p_dev_rec->sec_flags &= ~BTM_SEC_AUTHORIZED; |
| } |
| |
| if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { |
| if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && |
| (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { |
| /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case |
| */ |
| if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) { |
| p_dev_rec->sm4 |= BTM_SM4_UPGRADE; |
| } |
| p_dev_rec->sec_flags &= |
| ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED | |
| BTM_SEC_AUTHENTICATED); |
| BTM_TRACE_DEBUG("%s: sec_flags:0x%x", __func__, p_dev_rec->sec_flags); |
| } else { |
| /* If we already have a link key to the connected peer, is it secure |
| * enough? */ |
| btm_sec_check_upgrade(p_dev_rec, is_originator); |
| } |
| } |
| |
| BTM_TRACE_EVENT( |
| "%s() PSM:%d Handle:%d State:%d Flags: 0x%x Required: 0x%x Service ID:%d", |
| __func__, psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, |
| p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); |
| |
| rc = btm_sec_execute_procedure(p_dev_rec); |
| if (rc != BTM_CMD_STARTED) { |
| p_dev_rec->p_callback = NULL; |
| (*p_callback)(&bd_addr, transport, p_dev_rec->p_ref_data, (uint8_t)rc); |
| } |
| |
| return (rc); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_mx_access_request |
| * |
| * Description This function is called by all Multiplexing Protocols during |
| * establishing connection to or from peer device to grant |
| * permission to establish application connection. |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * psm - L2CAP PSM |
| * is_originator - true if protocol above L2CAP originates |
| * connection |
| * mx_proto_id - protocol ID of the multiplexer |
| * mx_chan_id - multiplexer channel to reach application |
| * p_callback - Pointer to callback function called if |
| * this function returns PENDING after required |
| * procedures are completed |
| * p_ref_data - Pointer to any reference data needed by the |
| * the callback function. |
| * |
| * Returns BTM_CMD_STARTED |
| * |
| ******************************************************************************/ |
| tBTM_STATUS btm_sec_mx_access_request(const RawAddress& bd_addr, uint16_t psm, |
| bool is_originator, uint32_t mx_proto_id, |
| uint32_t mx_chan_id, |
| tBTM_SEC_CALLBACK* p_callback, |
| void* p_ref_data) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| tBTM_SEC_SERV_REC* p_serv_rec; |
| tBTM_STATUS rc; |
| uint16_t security_required; |
| bool transport = false; /* should check PSM range in LE connection oriented |
| L2CAP connection */ |
| |
| BTM_TRACE_DEBUG("%s() is_originator: %d", __func__, is_originator); |
| /* Find or get oldest record */ |
| p_dev_rec = btm_find_or_alloc_dev(bd_addr); |
| |
| /* Find the service record for the PSM */ |
| p_serv_rec = |
| btm_sec_find_mx_serv(is_originator, psm, mx_proto_id, mx_chan_id); |
| |
| /* If there is no application registered with this PSM do not allow connection |
| */ |
| if (!p_serv_rec) { |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED); |
| |
| BTM_TRACE_ERROR( |
| "Security Manager: MX service not found PSM:%d Proto:%d SCN:%d", psm, |
| mx_proto_id, mx_chan_id); |
| return BTM_NO_RESOURCES; |
| } |
| |
| if ((btm_cb.security_mode == BTM_SEC_MODE_SC) && |
| (!btm_sec_is_serv_level0(psm))) { |
| security_required = btm_sec_set_serv_level4_flags( |
| p_serv_rec->security_flags, is_originator); |
| } else { |
| security_required = p_serv_rec->security_flags; |
| } |
| |
| /* there are some devices (moto phone) which connects to several services at |
| * the same time */ |
| /* we will process one after another */ |
| if ((p_dev_rec->p_callback) || |
| (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)) { |
| BTM_TRACE_EVENT("%s() service PSM:%d Proto:%d SCN:%d delayed state: %s", |
| __func__, psm, mx_proto_id, mx_chan_id, |
| btm_pair_state_descr(btm_cb.pairing_state)); |
| |
| rc = BTM_CMD_STARTED; |
| |
| if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || |
| btm_cb.security_mode == BTM_SEC_MODE_NONE || |
| btm_cb.security_mode == BTM_SEC_MODE_SERVICE || |
| btm_cb.security_mode == BTM_SEC_MODE_LINK) || |
| (BTM_SM4_KNOWN == p_dev_rec->sm4) || |
| (BTM_SEC_IS_SM4(p_dev_rec->sm4) && |
| (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == false))) { |
| /* legacy mode - local is legacy or local is lisbon/peer is legacy |
| * or SM4 with no possibility of link key upgrade */ |
| if (is_originator) { |
| if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == |
| BTM_SEC_OUT_AUTHENTICATE) && |
| btm_dev_authenticated(p_dev_rec))) || |
| ((((security_required & BTM_SEC_OUT_FLAGS) == |
| (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && |
| btm_dev_encrypted(p_dev_rec)))) { |
| rc = BTM_SUCCESS; |
| } |
| } else { |
| if (((security_required & BTM_SEC_IN_FLAGS) == 0) || |
| ((((security_required & BTM_SEC_IN_FLAGS) == |
| BTM_SEC_IN_AUTHENTICATE) && |
| btm_dev_authenticated(p_dev_rec))) || |
| (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) && |
| (btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec))) || |
| (((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_AUTHENTICATE)) && |
| ((btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec)) && |
| btm_dev_authenticated(p_dev_rec))) || |
| (((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT)) && |
| ((btm_dev_authorized(p_dev_rec) || |
| btm_serv_trusted(p_dev_rec, p_serv_rec)) && |
| btm_dev_encrypted(p_dev_rec))) || |
| ((((security_required & BTM_SEC_IN_FLAGS) == |
| (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && |
| btm_dev_encrypted(p_dev_rec)))) { |
| // Check for 16 digits (or MITM) |
| if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || |
| (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == |
| BTM_SEC_IN_MIN_16_DIGIT_PIN) && |
| btm_dev_16_digit_authenticated(p_dev_rec))) { |
| rc = BTM_SUCCESS; |
| } |
| } |
| } |
| if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && |
| (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { |
| rc = BTM_CMD_STARTED; |
| } |
| } |
| |
| if (rc == BTM_SUCCESS) { |
| BTM_TRACE_EVENT("%s: allow to bypass, checking authorization", __func__); |
| /* the security in BTM_SEC_IN_FLAGS is fullfilled so far, check the |
| * requirements in */ |
| /* btm_sec_execute_procedure */ |
| if ((is_originator && |
| (p_serv_rec->security_flags & BTM_SEC_OUT_AUTHORIZE)) || |
| (!is_originator && |
| (p_serv_rec->security_flags & BTM_SEC_IN_AUTHORIZE))) { |
| BTM_TRACE_EVENT("%s: still need authorization", __func__); |
| rc = BTM_CMD_STARTED; |
| } |
| } |
| |
| /* Check whether there is a pending security procedure, if so we should |
| * always queue */ |
| /* the new security request */ |
| if (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE) { |
| BTM_TRACE_EVENT("%s: There is a pending security procedure", __func__); |
| rc = BTM_CMD_STARTED; |
| } |
| if (rc == BTM_CMD_STARTED) { |
| BTM_TRACE_EVENT("%s: call btm_sec_queue_mx_request", __func__); |
| btm_sec_queue_mx_request(bd_addr, psm, is_originator, mx_proto_id, |
| mx_chan_id, p_callback, p_ref_data); |
| } else /* rc == BTM_SUCCESS */ |
| { |
| /* access granted */ |
| if (p_callback) { |
| (*p_callback)(&bd_addr, transport, p_ref_data, (uint8_t)rc); |
| } |
| } |
| |
| BTM_TRACE_EVENT("%s: return with rc = 0x%02x in delayed state %s", __func__, |
| rc, btm_pair_state_descr(btm_cb.pairing_state)); |
| return rc; |
| } |
| |
| if ((!is_originator) && ((security_required & BTM_SEC_MODE4_LEVEL4) || |
| (btm_cb.security_mode == BTM_SEC_MODE_SC))) { |
| bool local_supports_sc = |
| controller_get_interface()->supports_secure_connections(); |
| /* acceptor receives service connection establishment Request for */ |
| /* Secure Connections Only service */ |
| if (!(local_supports_sc) || |
| !(p_dev_rec->remote_supports_secure_connections)) { |
| BTM_TRACE_DEBUG("%s: SC only service,local_support_for_sc %d,", |
| "remote_support_for_sc %d: fail pairing", __func__, |
| local_supports_sc, |
| p_dev_rec->remote_supports_secure_connections); |
| |
| if (p_callback) |
| (*p_callback)(&bd_addr, transport, (void*)p_ref_data, |
| BTM_MODE4_LEVEL4_NOT_SUPPORTED); |
| |
| return (BTM_MODE4_LEVEL4_NOT_SUPPORTED); |
| } |
| } |
| |
| p_dev_rec->p_cur_service = p_serv_rec; |
| p_dev_rec->security_required = security_required; |
| |
| if (btm_cb.security_mode == BTM_SEC_MODE_SP || |
| btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || |
| btm_cb.security_mode == BTM_SEC_MODE_SC) { |
| if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { |
| if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && |
| (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { |
| /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case |
| */ |
| if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) { |
| p_dev_rec->sm4 |= BTM_SM4_UPGRADE; |
| } |
| |
| p_dev_rec->sec_flags &= |
| ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED | |
| BTM_SEC_AUTHENTICATED); |
| BTM_TRACE_DEBUG("%s: sec_flags:0x%x", __func__, p_dev_rec->sec_flags); |
| } else { |
| /* If we already have a link key, check if that link key is good enough |
| */ |
| btm_sec_check_upgrade(p_dev_rec, is_originator); |
| } |
| } |
| } |
| |
| p_dev_rec->is_originator = is_originator; |
| p_dev_rec->p_callback = p_callback; |
| p_dev_rec->p_ref_data = p_ref_data; |
| |
| /* Although authentication and encryption are per connection */ |
| /* authorization is per access request. For example when serial connection */ |
| /* is up and authorized and client requests to read file (access to other */ |
| /* scn, we need to request user's permission again. */ |
| p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED); |
| |
| BTM_TRACE_EVENT( |
| "%s() proto_id:%d chan_id:%d State:%d Flags:0x%x Required:0x%x Service " |
| "ID:%d", |
| __func__, mx_proto_id, mx_chan_id, p_dev_rec->sec_state, |
| p_dev_rec->sec_flags, p_dev_rec->security_required, |
| p_dev_rec->p_cur_service->service_id); |
| |
| rc = btm_sec_execute_procedure(p_dev_rec); |
| if (rc != BTM_CMD_STARTED) { |
| if (p_callback) { |
| p_dev_rec->p_callback = NULL; |
| (*p_callback)(&bd_addr, transport, p_ref_data, (uint8_t)rc); |
| } |
| } |
| |
| return rc; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_conn_req |
| * |
| * Description This function is when the peer device is requesting |
| * connection |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_conn_req(const RawAddress& bda, uint8_t* dc) { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda); |
| |
| /* Some device may request a connection before we are done with the HCI_Reset |
| * sequence */ |
| if (!controller_get_interface()->get_is_ready()) { |
| BTM_TRACE_EVENT("Security Manager: connect request when device not ready"); |
| btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE); |
| return; |
| } |
| |
| /* Security guys wants us not to allow connection from not paired devices */ |
| |
| /* Check if connection is allowed for only paired devices */ |
| if (btm_cb.connect_only_paired) { |
| if (!p_dev_rec || !(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)) { |
| BTM_TRACE_EVENT( |
| "Security Manager: connect request from non-paired device"); |
| btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE); |
| return; |
| } |
| } |
| |
| #if (BTM_ALLOW_CONN_IF_NONDISCOVER == FALSE) |
| /* If non-discoverable, only allow known devices to connect */ |
| if (btm_cb.btm_inq_vars.discoverable_mode == BTM_NON_DISCOVERABLE) { |
| if (!p_dev_rec) { |
| BTM_TRACE_EVENT( |
| "Security Manager: connect request from not paired device"); |
| btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE); |
| return; |
| } |
| } |
| #endif |
| |
| if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && |
| (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && |
| (btm_cb.pairing_bda == bda)) { |
| BTM_TRACE_EVENT( |
| "Security Manager: reject connect request from bonding device"); |
| |
| /* incoming connection from bonding device is rejected */ |
| btm_cb.pairing_flags |= BTM_PAIR_FLAGS_REJECTED_CONNECT; |
| btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE); |
| return; |
| } |
| |
| /* Host is not interested or approved connection. Save BDA and DC and */ |
| /* pass request to L2CAP */ |
| btm_cb.connecting_bda = bda; |
| memcpy(btm_cb.connecting_dc, dc, DEV_CLASS_LEN); |
| |
| if (l2c_link_hci_conn_req(bda)) { |
| if (!p_dev_rec) { |
| /* accept the connection -> allocate a device record */ |
| p_dev_rec = btm_sec_alloc_dev(bda); |
| } |
| if (p_dev_rec) { |
| p_dev_rec->sm4 |= BTM_SM4_CONN_PEND; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_bond_cancel_complete |
| * |
| * Description This function is called to report bond cancel complete |
| * event. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static void btm_sec_bond_cancel_complete(void) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| |
| if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) || |
| (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && |
| BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) || |
| (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME && |
| BTM_PAIR_FLAGS_WE_CANCEL_DD & btm_cb.pairing_flags)) { |
| /* for dedicated bonding in legacy mode, authentication happens at "link |
| * level" |
| * btm_sec_connected is called with failed status. |
| * In theory, the code that handles is_pairing_device/true should clean out |
| * security related code. |
| * However, this function may clean out the security related flags and |
| * btm_sec_connected would not know |
| * this function also needs to do proper clean up. |
| */ |
| p_dev_rec = btm_find_dev(btm_cb.pairing_bda); |
| if (p_dev_rec != NULL) p_dev_rec->security_required = BTM_SEC_NONE; |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| |
| /* Notify application that the cancel succeeded */ |
| if (btm_cb.api.p_bond_cancel_cmpl_callback) |
| btm_cb.api.p_bond_cancel_cmpl_callback(BTM_SUCCESS); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_create_conn_cancel_complete |
| * |
| * Description This function is called when the command complete message |
| * is received from the HCI for the create connection cancel |
| * command. |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_create_conn_cancel_complete(uint8_t* p) { |
| uint8_t status; |
| |
| STREAM_TO_UINT8(status, p); |
| BTM_TRACE_EVENT("btm_create_conn_cancel_complete(): in State: %s status:%d", |
| btm_pair_state_descr(btm_cb.pairing_state), status); |
| |
| /* if the create conn cancel cmd was issued by the bond cancel, |
| ** the application needs to be notified that bond cancel succeeded |
| */ |
| switch (status) { |
| case HCI_SUCCESS: |
| btm_sec_bond_cancel_complete(); |
| break; |
| case HCI_ERR_CONNECTION_EXISTS: |
| case HCI_ERR_NO_CONNECTION: |
| default: |
| /* Notify application of the error */ |
| if (btm_cb.api.p_bond_cancel_cmpl_callback) |
| btm_cb.api.p_bond_cancel_cmpl_callback(BTM_ERR_PROCESSING); |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_check_pending_reqs |
| * |
| * Description This function is called at the end of the security procedure |
| * to let L2CAP and RFCOMM know to re-submit any pending |
| * requests |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_check_pending_reqs(void) { |
| if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) { |
| /* First, resubmit L2CAP requests */ |
| if (btm_cb.sec_req_pending) { |
| btm_cb.sec_req_pending = false; |
| l2cu_resubmit_pending_sec_req(nullptr); |
| } |
| |
| /* Now, re-submit anything in the mux queue */ |
| fixed_queue_t* bq = btm_cb.sec_pending_q; |
| |
| btm_cb.sec_pending_q = fixed_queue_new(SIZE_MAX); |
| |
| tBTM_SEC_QUEUE_ENTRY* p_e; |
| while ((p_e = (tBTM_SEC_QUEUE_ENTRY*)fixed_queue_try_dequeue(bq)) != NULL) { |
| /* Check that the ACL is still up before starting security procedures */ |
| if (btm_bda_to_acl(p_e->bd_addr, p_e->transport) != NULL) { |
| if (p_e->psm != 0) { |
| BTM_TRACE_EVENT( |
| "%s PSM:0x%04x Is_Orig:%u mx_proto_id:%u mx_chan_id:%u", __func__, |
| p_e->psm, p_e->is_orig, p_e->mx_proto_id, p_e->mx_chan_id); |
| |
| btm_sec_mx_access_request(p_e->bd_addr, p_e->psm, p_e->is_orig, |
| p_e->mx_proto_id, p_e->mx_chan_id, |
| p_e->p_callback, p_e->p_ref_data); |
| } else { |
| BTM_SetEncryption(p_e->bd_addr, p_e->transport, p_e->p_callback, |
| p_e->p_ref_data, p_e->sec_act); |
| } |
| } |
| |
| osi_free(p_e); |
| } |
| fixed_queue_free(bq, NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_init |
| * |
| * Description This function is on the SEC startup |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_init(uint8_t sec_mode) { |
| btm_cb.security_mode = sec_mode; |
| btm_cb.pairing_bda = RawAddress::kAny; |
| btm_cb.max_collision_delay = BTM_SEC_MAX_COLLISION_DELAY; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_device_down |
| * |
| * Description This function should be called when device is disabled or |
| * turned off |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_device_down(void) { |
| BTM_TRACE_EVENT("%s() State: %s", __func__, |
| btm_pair_state_descr(btm_cb.pairing_state)); |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_dev_reset |
| * |
| * Description This function should be called after device reset |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_dev_reset(void) { |
| if (controller_get_interface()->supports_simple_pairing()) { |
| /* set the default IO capabilities */ |
| btm_cb.devcb.loc_io_caps = BTM_LOCAL_IO_CAPS; |
| /* add mx service to use no security */ |
| BTM_SetSecurityLevel(false, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, |
| BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0); |
| } else { |
| btm_cb.security_mode = BTM_SEC_MODE_SERVICE; |
| } |
| |
| BTM_TRACE_DEBUG("btm_sec_dev_reset sec mode: %d", btm_cb.security_mode); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_abort_access_req |
| * |
| * Description This function is called by the L2CAP or RFCOMM to abort |
| * the pending operation. |
| * |
| * Parameters: bd_addr - Address of the peer device |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_abort_access_req(const RawAddress& bd_addr) { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr); |
| |
| if (!p_dev_rec) return; |
| |
| if ((p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) && |
| (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING)) |
| return; |
| |
| p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; |
| p_dev_rec->p_callback = NULL; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_dd_create_conn |
| * |
| * Description This function is called to create the ACL connection for |
| * the dedicated boding process |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| static tBTM_STATUS btm_sec_dd_create_conn(tBTM_SEC_DEV_REC* p_dev_rec) { |
| tL2C_LCB* p_lcb = |
| l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); |
| if (p_lcb && (p_lcb->link_state == LST_CONNECTED || |
| p_lcb->link_state == LST_CONNECTING)) { |
| BTM_TRACE_WARNING("%s Connection already exists", __func__); |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ); |
| return BTM_CMD_STARTED; |
| } |
| |
| /* Make sure an L2cap link control block is available */ |
| if (!p_lcb && |
| (p_lcb = l2cu_allocate_lcb(p_dev_rec->bd_addr, true, |
| BT_TRANSPORT_BR_EDR)) == NULL) { |
| LOG(WARNING) << "Security Manager: failed allocate LCB " |
| << p_dev_rec->bd_addr; |
| |
| return (BTM_NO_RESOURCES); |
| } |
| |
| /* set up the control block to indicated dedicated bonding */ |
| btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; |
| |
| if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == false) { |
| LOG(WARNING) << "Security Manager: failed create allocate LCB " |
| << p_dev_rec->bd_addr; |
| |
| l2cu_release_lcb(p_lcb); |
| return (BTM_NO_RESOURCES); |
| } |
| |
| btm_acl_update_busy_level(BTM_BLI_PAGE_EVT); |
| |
| VLOG(1) << "Security Manager: " << p_dev_rec->bd_addr; |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ); |
| |
| return (BTM_CMD_STARTED); |
| } |
| |
| bool is_state_getting_name(void* data, void* context) { |
| tBTM_SEC_DEV_REC* p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(data); |
| |
| if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME) { |
| return false; |
| } |
| return true; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function btm_sec_rmt_name_request_complete |
| * |
| * Description This function is called when remote name was obtained from |
| * the peer device |
| * |
| * Returns void |
| * |
| ******************************************************************************/ |
| void btm_sec_rmt_name_request_complete(const RawAddress* p_bd_addr, |
| uint8_t* p_bd_name, uint8_t status) { |
| tBTM_SEC_DEV_REC* p_dev_rec; |
| int i; |
| DEV_CLASS dev_class; |
| uint8_t old_sec_state; |
| |
| BTM_TRACE_EVENT("btm_sec_rmt_name_request_complete"); |
| if ((!p_bd_addr && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) || |
| (p_bd_addr && !BTM_ACL_IS_CONNECTED(*p_bd_addr))) { |
| btm_acl_resubmit_page(); |
| } |
| |
| /* If remote name request failed, p_bd_addr is null and we need to search */ |
| /* based on state assuming that we are doing 1 at a time */ |
| if (p_bd_addr) |
| p_dev_rec = btm_find_dev(*p_bd_addr); |
| else { |
| list_node_t* node = |
| list_foreach(btm_cb.sec_dev_rec, is_state_getting_name, NULL); |
| if (node != NULL) { |
| p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(list_node(node)); |
| p_bd_addr = &p_dev_rec->bd_addr; |
| } else { |
| p_dev_rec = NULL; |
| } |
| } |
| |
| /* Commenting out trace due to obf/compilation problems. |
| */ |
| if (!p_bd_name) p_bd_name = (uint8_t*)""; |
| |
| if (p_dev_rec) { |
| BTM_TRACE_EVENT( |
| "%s PairState: %s RemName: %s status: %d State:%d p_dev_rec: " |
| "0x%08x ", |
| __func__, btm_pair_state_descr(btm_cb.pairing_state), p_bd_name, status, |
| p_dev_rec->sec_state, p_dev_rec); |
| } else { |
| BTM_TRACE_EVENT("%s PairState: %s RemName: %s status: %d", __func__, |
| btm_pair_state_descr(btm_cb.pairing_state), p_bd_name, |
| status); |
| } |
| |
| if (p_dev_rec) { |
| old_sec_state = p_dev_rec->sec_state; |
| if (status == HCI_SUCCESS) { |
| strlcpy((char*)p_dev_rec->sec_bd_name, (char*)p_bd_name, |
| BTM_MAX_REM_BD_NAME_LEN); |
| p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; |
| BTM_TRACE_EVENT("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x", |
| p_dev_rec->sec_flags); |
| } else { |
| /* Notify all clients waiting for name to be resolved even if it failed so |
| * clients can continue */ |
| p_dev_rec->sec_bd_name[0] = 0; |
| } |
| |
| if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME) |
| p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; |
| |
| /* Notify all clients waiting for name to be resolved */ |
| for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { |
| if (btm_cb.p_rmt_name_callback[i] && p_bd_addr) |
| (*btm_cb.p_rmt_name_callback[i])(*p_bd_addr, p_dev_rec->dev_class, |
| p_dev_rec->sec_bd_name); |
| } |
| } else { |
| dev_class[0] = 0; |
| dev_class[1] = 0; |
| dev_class[2] = 0; |
| |
| /* Notify all clients waiting for name to be resolved even if not found so |
| * clients can continue */ |
| for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) { |
| if (btm_cb.p_rmt_name_callback[i] && p_bd_addr) |
| (*btm_cb.p_rmt_name_callback[i])(*p_bd_addr, dev_class, (uint8_t*)""); |
| } |
| |
| return; |
| } |
| |
| /* If we were delaying asking UI for a PIN because name was not resolved, ask |
| * now */ |
| if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr && |
| (btm_cb.pairing_bda == *p_bd_addr)) { |
| BTM_TRACE_EVENT( |
| "%s() delayed pin now being requested flags:0x%x, " |
| "(p_pin_callback=0x%p)", |
| __func__, btm_cb.pairing_flags, btm_cb.api.p_pin_callback); |
| |
| if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0 && |
| btm_cb.api.p_pin_callback) { |
| BTM_TRACE_EVENT("%s() calling pin_callback", __func__); |
| btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; |
| (*btm_cb.api.p_pin_callback)( |
| p_dev_rec->bd_addr, p_dev_rec->dev_class, p_bd_name, |
| (p_dev_rec->p_cur_service == NULL) |
| ? false |
| : (p_dev_rec->p_cur_service->security_flags & |
| BTM_SEC_IN_MIN_16_DIGIT_PIN)); |
| } |
| |
| /* Set the same state again to force the timer to be restarted */ |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_PIN); |
| return; |
| } |
| |
| /* Check if we were delaying bonding because name was not resolved */ |
| if (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) { |
| if (p_bd_addr && btm_cb.pairing_bda == *p_bd_addr) { |
| BTM_TRACE_EVENT("%s() continue bonding sm4: 0x%04x, status:0x%x", |
| __func__, p_dev_rec->sm4, status); |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_CANCEL_DD) { |
| btm_sec_bond_cancel_complete(); |
| return; |
| } |
| |
| if (status != HCI_SUCCESS) { |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| |
| if (btm_cb.api.p_auth_complete_callback) |
| (*btm_cb.api.p_auth_complete_callback)( |
| p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, |
| status); |
| return; |
| } |
| |
| /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is |
| * not reported */ |
| if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) { |
| /* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not |
| * set.*/ |
| /* If it is set, there may be a race condition */ |
| BTM_TRACE_DEBUG("%s IS_SM4_UNKNOWN Flags:0x%04x", __func__, |
| btm_cb.pairing_flags); |
| if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0) |
| p_dev_rec->sm4 |= BTM_SM4_KNOWN; |
| } |
| |
| BTM_TRACE_DEBUG("%s, SM4 Value: %x, Legacy:%d,IS SM4:%d, Unknown:%d", |
| __func__, p_dev_rec->sm4, |
| BTM_SEC_IS_SM4_LEGACY(p_dev_rec->sm4), |
| BTM_SEC_IS_SM4(p_dev_rec->sm4), |
| BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)); |
| |
| /* BT 2.1 or carkit, bring up the connection to force the peer to request |
| *PIN. |
| ** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if |
| *needed) |
| */ |
| if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) || |
| !btm_sec_check_prefetch_pin(p_dev_rec)) { |
| /* if we rejected incoming connection request, we have to wait |
| * HCI_Connection_Complete event */ |
| /* before originating */ |
| if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) { |
| BTM_TRACE_WARNING( |
| "%s: waiting HCI_Connection_Complete after rejecting connection", |
| __func__); |
| } |
| /* Both we and the peer are 2.1 - continue to create connection */ |
| else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) { |
| BTM_TRACE_WARNING("%s: failed to start connection", __func__); |
| |
| btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); |
| |
| if (btm_cb.api.p_auth_complete_callback) { |
| (*btm_cb.api.p_auth_complete_callback)( |
| p_dev_rec->bd_addr, p_dev_rec->dev_class, |
| p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); |
| } |
| } |
| } |
| return; |
| } else { |
| BTM_TRACE_WARNING("%s: wrong BDA, retry with pairing BDA", __func__); |
| if (BTM_ReadRemoteDeviceName(btm_cb.pairing_bda, NULL, |
| BT_TRANSPORT_BR_EDR) != BTM_CMD_STARTED) { |
| BTM_TRACE_ERROR("%s: failed to start remote name request", __func__); |
| if (btm_cb.api.p_auth_complete_callback) { |
| (*btm_cb.api.p_auth_complete_callback)( |
| p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, |
| HCI_ERR_MEMORY_FULL); |
| } |
| }; |
| return; |
| } |
| } |
| |
| /* check if we were delaying link_key_callback because name was not resolved |
|