blob: 633e55360bf541bfee6214f642f03f1c73eb15b7 [file] [log] [blame]
/******************************************************************************
*
* 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 <string.h>
#include "device/include/controller.h"
#include "osi/include/log.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"
#if (BT_USE_TRACES == TRUE && BT_TRACE_VERBOSE == FALSE)
/* needed for sprintf() */
#include <stdio.h>
#endif
#if BLE_INCLUDED == TRUE
#include "gatt_int.h"
#endif
#define BTM_SEC_MAX_COLLISION_DELAY (5000)
extern fixed_queue_t *btu_general_alarm_queue;
#ifdef APPL_AUTH_WRITE_EXCEPTION
BOOLEAN (APPL_AUTH_WRITE_EXCEPTION)(BD_ADDR 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 (BOOLEAN is_originator, UINT16 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 is_originator, UINT16 psm,
UINT32 mx_proto_id,
UINT32 mx_chan_id);
static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN 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);
#if (BT_USE_TRACES == TRUE)
static char *btm_pair_state_descr (tBTM_PAIRING_STATE state);
#endif
static void btm_sec_check_pending_reqs(void);
static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig,
UINT32 mx_proto_id, UINT32 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 BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec);
static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec);
BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]);
static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason, UINT16 conn_handle);
UINT8 btm_sec_start_role_switch (tBTM_SEC_DEV_REC *p_dev_rec);
tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state);
static BOOLEAN btm_sec_set_security_level ( CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id,
UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id,
UINT32 mx_chan_id);
static BOOLEAN btm_dev_authenticated(tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_dev_encrypted(tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_dev_authorized(tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_serv_trusted(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_SEC_SERV_REC *p_serv_rec);
static BOOLEAN btm_sec_is_serv_level0 (UINT16 psm);
static UINT16 btm_sec_set_serv_level4_flags (UINT16 cur_security, BOOLEAN is_originator);
static BOOLEAN btm_sec_queue_encrypt_request (BD_ADDR 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 encr_enable);
static BOOLEAN btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC *p_dev_rec);
static BOOLEAN btm_sec_is_master(tBTM_SEC_DEV_REC *p_dev_rec);
/* TRUE - authenticated link key is possible */
static const BOOLEAN 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 BOOLEAN TRUE or FALSE
**
*******************************************************************************/
static BOOLEAN 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 BOOLEAN TRUE or FALSE
**
*******************************************************************************/
static BOOLEAN 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 BOOLEAN TRUE or FALSE
**
*******************************************************************************/
static BOOLEAN 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 BOOLEAN TRUE or FALSE
**
*******************************************************************************/
static BOOLEAN 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 BOOLEAN TRUE or FALSE
**
*******************************************************************************/
static BOOLEAN 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
**
*******************************************************************************/
BOOLEAN BTM_SecRegister(tBTM_APPL_INFO *p_cb_info)
{
#if BLE_INCLUDED == TRUE
BT_OCTET16 temp_value = {0};
#endif
BTM_TRACE_EVENT("%s application registered", __func__);
#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
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)
{
#if SMP_INCLUDED == TRUE
BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )", __func__);
SMP_Register(btm_proc_smp_cback);
#endif
/* 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__);
}
#endif
btm_cb.api = *p_cb_info;
#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
LOG_INFO(LOG_TAG, "%s btm_cb.api.p_le_callback = 0x%p ", __func__, btm_cb.api.p_le_callback);
#endif
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
**
*******************************************************************************/
BOOLEAN 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
**
*******************************************************************************/
BOOLEAN 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
**
*******************************************************************************/
BOOLEAN 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 BOOLEAN TRUE or FALSE is device found
**
*******************************************************************************/
BOOLEAN BTM_GetSecurityFlags (BD_ADDR bd_addr, UINT8 * p_sec_flags)
{
tBTM_SEC_DEV_REC *p_dev_rec;
if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL)
{
*p_sec_flags = (UINT8) 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 BOOLEAN TRUE or FALSE is device found
**
*******************************************************************************/
BOOLEAN BTM_GetSecurityFlagsByTransport (BD_ADDR bd_addr, UINT8 * p_sec_flags,
tBT_TRANSPORT transport)
{
tBTM_SEC_DEV_REC *p_dev_rec;
if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL)
{
if (transport == BT_TRANSPORT_BR_EDR)
*p_sec_flags = (UINT8) p_dev_rec->sec_flags;
else
*p_sec_flags = (UINT8) (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 pin_type, PIN_CODE pin_code, UINT8 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 (BOOLEAN allow_pairing, BOOLEAN 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 or FALSE) whether or not the device
** TRUE means that the device should treat Mode 4 Level 0 services as
** services of other levels. (Secure_connections_only_mode)
** FALSE means that the device should provide default treatment for
** Mode 4 Level 0 services.
**
** Returns void
**
*******************************************************************************/
void BTM_SetSecureConnectionsOnly (BOOLEAN secure_connections_only_mode)
{
BTM_TRACE_API("%s: Mode : %u", __FUNCTION__,
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) 0xffff)
/*******************************************************************************
**
** Function BTM_SetSecurityLevel
**
** Description Register service security level with Security Manager
**
** Parameters: is_originator - TRUE if originating the connection, FALSE if not
** 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
**
*******************************************************************************/
BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id,
UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id,
UINT32 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, FALSE if not
** 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 BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id,
UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id,
UINT32 mx_chan_id)
{
tBTM_SEC_SERV_REC *p_srec;
UINT16 index;
UINT16 first_unused_record = BTM_NO_AVAIL_SEC_SERVICES;
BOOLEAN record_allocated = FALSE;
BOOLEAN is_originator;
#if (L2CAP_UCD_INCLUDED == TRUE)
BOOLEAN 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,
BTM_SEC_SERVICE_NAME_LEN) ||
!strncmp (p_name, (char *) p_srec->term_service_name,
BTM_SEC_SERVICE_NAME_LEN)))
#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);
#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);
#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)(BTM_SEC_IN_USE);
p_srec->ucd_security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE);
}
else
{
p_srec->security_flags |= (UINT16)(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)(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 BTM_SecClrService (UINT8 service_id)
{
tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0];
UINT8 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 btm_sec_clr_service_by_psm (UINT16 psm)
{
tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0];
UINT8 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 (BD_ADDR bda)
{
tBTM_SEC_DEV_REC *p_dev_rec;
if ((p_dev_rec = btm_find_dev (bda)) == 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)
{
BTM_TRACE_DEBUG ("btm_sec_clr_auth_service_by_psm [clearing device: %02x:%02x:%02x:%02x:%02x:%02x]",
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
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)
**
*******************************************************************************/
void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, UINT8 *p_pin, UINT32 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 (memcmp (bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) != 0)
{
BTM_TRACE_ERROR ("BTM_PINCodeReply() - Wrong BD Addr");
return;
}
if ((p_dev_rec = btm_find_dev (bd_addr)) == 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)
**
** Note: After 2.1 parameters are not used and preserved here not to change API
*******************************************************************************/
tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
{
tBTM_SEC_DEV_REC *p_dev_rec;
tBTM_STATUS status;
UINT8 *p_features;
UINT8 ii;
tACL_CONN *p= btm_bda_to_acl(bd_addr, transport);
BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
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);
}
if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == 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))
#if (BLE_INCLUDED == TRUE)
||((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE
&& (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))
#endif
)
{
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);
}
memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN);
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 BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
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);
}
#endif
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->features[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)
{
if (!btm_sec_start_authentication (p_dev_rec))
return(BTM_NO_RESOURCES);
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);
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);
}
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);
return BTM_CMD_STARTED;
}
/* both local and peer are 2.1 */
status = btm_sec_dd_create_conn(p_dev_rec);
if (status != BTM_CMD_STARTED)
{
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)
**
** Note: After 2.1 parameters are not used and preserved here not to change API
*******************************************************************************/
tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
{
#if SMP_INCLUDED == TRUE
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;
}
#endif
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)
**
** Note: After 2.1 parameters are not used and preserved here not to change API
*******************************************************************************/
tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
{
tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
#if BLE_INCLUDED == TRUE
if (BTM_UseLeLink(bd_addr))
transport = BT_TRANSPORT_LE;
#endif
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 (BD_ADDR 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);
if (((p_dev_rec = btm_find_dev (bd_addr)) == NULL)
|| (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) )
return BTM_UNKNOWN_ADDR;
#if SMP_INCLUDED == TRUE
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;
}
#endif
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)
{
if (btsnd_hcic_create_conn_cancel(bd_addr))
return BTM_CMD_STARTED;
return BTM_NO_RESOURCES;
}
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 (BD_ADDR bd_addr, LINK_KEY link_key)
{
tBTM_SEC_DEV_REC *p_dev_rec;
if (((p_dev_rec = btm_find_dev (bd_addr)) != 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 (BD_ADDR 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 (BD_ADDR 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)
#if BLE_INCLUDED == TRUE
|| (transport == BT_TRANSPORT_LE && p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE)
#endif
)
{
/* 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 BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
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", __FUNCTION__);
}
}
else
#endif
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 reason, UINT16 conn_handle)
{
UINT8 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;
#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
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;
#endif
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 if (!btsnd_hcic_disconnect (conn_handle, reason))
{
/* could not send disconnect. restore old state */
p_dev_rec->sec_state = old_state;
status = BTM_NO_RESOURCES;
}
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, BD_ADDR 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)
|| (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) )
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)
{
if ((p_dev_rec = btm_find_dev (bd_addr)) != 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, BD_ADDR bd_addr, UINT32 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)
|| (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) )
{
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(BD_ADDR 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(BD_ADDR 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)
|| (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) )
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
**
*******************************************************************************/
tBTM_STATUS BTM_ReadLocalOobData(void)
{
tBTM_STATUS status = BTM_SUCCESS;
if (btsnd_hcic_read_local_oob_data() == FALSE)
status = BTM_NO_RESOURCES;
return status;
}
/*******************************************************************************
**
** 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, BD_ADDR 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 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c,
BT_OCTET16 r, UINT8 name_len)
{
UINT8 *p = p_data;
UINT16 len = 0;
#if BTM_MAX_LOC_BD_NAME_LEN > 0
UINT16 name_size;
UINT8 name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE;
#endif
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()->address);
len = BTM_OOB_MANDATORY_SIZE;
max_len -= len;
/* now optional part */
/* add Hash C */
UINT16 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;
}
#if BTM_MAX_LOC_BD_NAME_LEN > 0
name_size = name_len;
if (name_size > strlen(btm_cb.cfg.bd_name))
{
name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE;
name_size = (UINT16)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;
}
#endif
/* 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.
**
*******************************************************************************/
BOOLEAN BTM_BothEndsSupportSecureConnections(BD_ADDR 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.
**
*******************************************************************************/
BOOLEAN BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr)
{
tBTM_SEC_DEV_REC *p_dev_rec;
if ((p_dev_rec = btm_find_dev(bd_addr)) == NULL)
{
BTM_TRACE_WARNING("%s: unknown BDA: %08x%04x", __FUNCTION__,
(bd_addr[0]<<24) + (bd_addr[1]<<16) + (bd_addr[2]<<8) + bd_addr[3],
(bd_addr[4]<< 8) + bd_addr[5]);
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 * BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len)
{
UINT8 *p = p_data;
UINT16 max_len;
UINT8 len, type;
UINT8 *p_ret = NULL;
UINT8 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(BD_ADDR bd_addr, UINT8 service_id, UINT32 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 BOOLEAN
**
*******************************************************************************/
static BOOLEAN btm_sec_is_upgrade_possible(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator)
{
UINT16 mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM;
BOOLEAN 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, BOOLEAN 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;
memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
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 (BD_ADDR bd_addr, UINT16 psm, UINT16 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 security_required;
UINT16 old_security_required;
BOOLEAN old_is_originator;
tBTM_STATUS rc = BTM_SUCCESS;
BOOLEAN chk_acp_auth_done = FALSE;
BOOLEAN 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",
__FUNCTION__, security_required, is_originator, psm);
if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4))
{
BOOLEAN 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", __FUNCTION__,
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", __FUNCTION__,
(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", __FUNCTION__, 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);
if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED)
{
p_dev_rec->p_callback = NULL;
(*p_callback) (bd_addr, transport, p_dev_rec->p_ref_data, (UINT8)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 (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator,
UINT32 mx_proto_id, UINT32 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 security_required;
BOOLEAN 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", __FUNCTION__);
/* 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", __FUNCTION__);
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", __FUNCTION__);
rc = BTM_CMD_STARTED;
}
if (rc == BTM_CMD_STARTED)
{
BTM_TRACE_EVENT("%s: call btm_sec_queue_mx_request", __FUNCTION__);
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)rc);
}
}
BTM_TRACE_EVENT("%s: return with rc = 0x%02x in delayed state %s", __FUNCTION__, 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)))
{
BOOLEAN 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",__FUNCTION__,
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", __FUNCTION__, 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);
if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED)
{
if (p_callback)
{
p_dev_rec->p_callback = NULL;
(*p_callback) (bd_addr,transport, p_ref_data, (UINT8)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 (UINT8 *bda, UINT8 *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)
&&(!memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN)))
{
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 */
memcpy (btm_cb.connecting_bda, bda, BD_ADDR_LEN);
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.
*/
if ((p_dev_rec = btm_find_dev (btm_cb.pairing_bda)) != 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 *p)
{
UINT8 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 (NULL);
}
/* 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);