Bluetooth: L2CAP: Handle incoming BR/EDR connection request

Enables L2CAP protocol definitions for connection request/response and
use it then to start handle incoming PSM connection request to valid
registered local PSM server. SDP PSM got no security restrictions.
The other PSM connections are validated against link encryption and
availability of SSP feature and if not matched are refused with
security error.

Change-Id: I429cf5dbce92300bd52639d5065e0144f8db4d13
Signed-off-by: Arkadiusz Lichwa <arkadiusz.lichwa@tieto.com>
This commit is contained in:
Arkadiusz Lichwa 2016-05-12 14:21:56 +02:00 committed by Johan Hedberg
commit 8b1c837e28
4 changed files with 138 additions and 0 deletions

View file

@ -126,6 +126,9 @@ struct bt_hci_cmd_hdr {
#define BT_LMP_LE 0x40
#define BT_LMP_REMOTE_EXT_FEATURES 0x80
/* Host features */
#define BT_LMP_HOST_SSP 0x01
/* LE features */
#define BT_HCI_LE_ENCRYPTION 0x01
#define BT_HCI_LE_CONN_PARAM_REQ_PROC 0x02

View file

@ -64,6 +64,10 @@ struct bt_conn_le {
#define lmp_ext_feat_capable(conn) \
((conn)->br.features[0][7] & BT_LMP_REMOTE_EXT_FEATURES)
/* Helper to validate SSP host support within retrieved remote LMP features */
#define lmp_ssp_host_supported(conn) \
((conn)->br.features[1][0] & BT_LMP_HOST_SSP)
struct bt_conn_br {
bt_addr_t dst;
uint8_t remote_io_capa;

View file

@ -50,6 +50,8 @@
#define L2CAP_BR_MIN_MTU 48
#define L2CAP_BR_PSM_SDP 0x0001
/*
* L2CAP extended feature mask:
* BR/EDR fixed channel support enabled
@ -99,6 +101,22 @@ struct bt_l2cap_chan *bt_l2cap_br_lookup_rx_cid(struct bt_conn *conn,
return NULL;
}
static struct bt_l2cap_chan *bt_l2cap_br_lookup_tx_cid(struct bt_conn *conn,
uint16_t cid)
{
struct bt_l2cap_chan *chan;
for (chan = conn->channels; chan; chan = chan->_next) {
struct bt_l2cap_br_chan *ch = BR_CHAN(chan);
if (ch->tx.cid == cid) {
return chan;
}
}
return NULL;
}
static struct bt_l2cap_br_chan*
l2cap_br_chan_alloc_cid(struct bt_conn *conn, struct bt_l2cap_chan *chan)
{
@ -367,6 +385,101 @@ static struct bt_l2cap_server *l2cap_br_server_lookup_psm(uint16_t psm)
return NULL;
}
static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
struct net_buf *buf)
{
struct bt_conn *conn = l2cap->chan.chan.conn;
struct bt_l2cap_chan *chan;
struct bt_l2cap_server *server;
struct bt_l2cap_conn_req *req = (void *)buf->data;
struct bt_l2cap_conn_rsp *rsp;
struct bt_l2cap_sig_hdr *hdr;
uint16_t psm, scid, dcid, result;
if (buf->len < sizeof(*req)) {
BT_ERR("Too small L2CAP conn req packet size");
return;
}
psm = sys_le16_to_cpu(req->psm);
scid = sys_le16_to_cpu(req->scid);
dcid = 0;
BT_DBG("psm 0x%02x scid 0x%04x", psm, scid);
buf = bt_l2cap_create_pdu(&br_sig);
if (!buf) {
return;
}
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->code = BT_L2CAP_CONN_RSP;
hdr->ident = ident;
hdr->len = sys_cpu_to_le16(sizeof(*rsp));
rsp = net_buf_add(buf, sizeof(*rsp));
memset(rsp, 0, sizeof(*rsp));
/* Check if there is a server registered */
server = l2cap_br_server_lookup_psm(psm);
if (!server) {
result = BT_L2CAP_ERR_PSM_NOT_SUPP;
goto done;
}
/*
* Report security violation for non SDP channel without encryption when
* remote supports SSP.
*/
if (psm != L2CAP_BR_PSM_SDP && lmp_ssp_host_supported(conn) &&
!conn->encrypt) {
result = BT_L2CAP_ERR_SEC_BLOCK;
goto done;
}
if (scid < L2CAP_BR_DYN_CID_START || scid > L2CAP_BR_DYN_CID_END) {
result = BT_L2CAP_ERR_INVALID_SCID;
goto done;
}
chan = bt_l2cap_br_lookup_tx_cid(conn, scid);
if (chan) {
result = BT_L2CAP_ERR_SCID_IN_USE;
goto done;
}
/*
* Request server to accept the new connection and allocate the
* channel.
*/
if (server->accept(conn, &chan) < 0) {
result = BT_L2CAP_ERR_NO_RESOURCES;
goto done;
}
l2cap_br_chan_add(conn, chan);
BR_CHAN(chan)->tx.cid = scid;
dcid = BR_CHAN(chan)->rx.cid;
/*
* TODO: Verify security level on link if this PSM channel requires
* higher security.
*/
result = BT_L2CAP_SUCCESS;
done:
rsp->dcid = sys_cpu_to_le16(dcid);
rsp->scid = req->scid;
rsp->result = sys_cpu_to_le16(result);
/* TODO: add command timeout guard */
bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf);
/* Disconnect link when security rules were violated */
if (result == BT_L2CAP_ERR_SEC_BLOCK) {
bt_conn_disconnect(conn, BT_HCI_ERR_AUTHENTICATION_FAIL);
}
}
int bt_l2cap_br_server_register(struct bt_l2cap_server *server)
{
if (server->psm < L2CAP_BR_PSM_START || !server->accept) {
@ -575,6 +688,9 @@ static void l2cap_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
case BT_L2CAP_DISCONN_REQ:
l2cap_br_disconn_req(l2cap, hdr->ident, buf);
break;
case BT_L2CAP_CONN_REQ:
l2cap_br_conn_req(l2cap, hdr->ident, buf);
break;
default:
BT_WARN("Unknown/Unsupported L2CAP PDU code 0x%02x", hdr->code);
l2cap_br_send_reject(chan->conn, hdr->ident,

View file

@ -55,6 +55,20 @@ struct bt_l2cap_cmd_reject_cid_data {
uint16_t dcid;
} __packed;
#define BT_L2CAP_CONN_REQ 0x02
struct bt_l2cap_conn_req {
uint16_t psm;
uint16_t scid;
} __packed;
#define BT_L2CAP_CONN_RSP 0x03
struct bt_l2cap_conn_rsp {
uint16_t dcid;
uint16_t scid;
uint16_t result;
uint16_t status;
} __packed;
#define BT_L2CAP_DISCONN_REQ 0x06
struct bt_l2cap_disconn_req {
uint16_t dcid;
@ -113,6 +127,7 @@ struct bt_l2cap_le_conn_req {
#define BT_L2CAP_SUCCESS 0x0000
#define BT_L2CAP_ERR_PSM_NOT_SUPP 0x0002
#define BT_L2CAP_ERR_SEC_BLOCK 0x0003
#define BT_L2CAP_ERR_NO_RESOURCES 0x0004
#define BT_L2CAP_ERR_AUTHENTICATION 0x0005
#define BT_L2CAP_ERR_AUTHORIZATION 0x0006