Commit 12bcbdd3 authored by Marcel Holtmann's avatar Marcel Holtmann

[Bluetooth] Add RFCOMM service level security

This patch adds the support for RFCOMM service level security. It
allows to request authentication and encryption before a RFCOMM
connection is finally established.
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent b67f0545
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#define RFCOMM_CONN_TIMEOUT (HZ * 30) #define RFCOMM_CONN_TIMEOUT (HZ * 30)
#define RFCOMM_DISC_TIMEOUT (HZ * 20) #define RFCOMM_DISC_TIMEOUT (HZ * 20)
#define RFCOMM_AUTH_TIMEOUT (HZ * 25)
#define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_DEFAULT_MTU 127
#define RFCOMM_DEFAULT_CREDITS 7 #define RFCOMM_DEFAULT_CREDITS 7
...@@ -198,14 +199,18 @@ struct rfcomm_dlc { ...@@ -198,14 +199,18 @@ struct rfcomm_dlc {
/* DLC and session flags */ /* DLC and session flags */
#define RFCOMM_RX_THROTTLED 0 #define RFCOMM_RX_THROTTLED 0
#define RFCOMM_TX_THROTTLED 1 #define RFCOMM_TX_THROTTLED 1
#define RFCOMM_MSC_PENDING 2 #define RFCOMM_TIMED_OUT 2
#define RFCOMM_TIMED_OUT 3 #define RFCOMM_MSC_PENDING 3
#define RFCOMM_AUTH_PENDING 4
#define RFCOMM_AUTH_ACCEPT 5
#define RFCOMM_AUTH_REJECT 6
/* Scheduling flags and events */ /* Scheduling flags and events */
#define RFCOMM_SCHED_STATE 0 #define RFCOMM_SCHED_STATE 0
#define RFCOMM_SCHED_RX 1 #define RFCOMM_SCHED_RX 1
#define RFCOMM_SCHED_TX 2 #define RFCOMM_SCHED_TX 2
#define RFCOMM_SCHED_TIMEO 3 #define RFCOMM_SCHED_TIMEO 3
#define RFCOMM_SCHED_AUTH 4
#define RFCOMM_SCHED_WAKEUP 31 #define RFCOMM_SCHED_WAKEUP 31
/* MSC exchange flags */ /* MSC exchange flags */
......
...@@ -47,10 +47,11 @@ ...@@ -47,10 +47,11 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h> #include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h> #include <net/bluetooth/rfcomm.h>
#define VERSION "1.4" #define VERSION "1.5"
#ifndef CONFIG_BT_RFCOMM_DEBUG #ifndef CONFIG_BT_RFCOMM_DEBUG
#undef BT_DBG #undef BT_DBG
...@@ -1094,6 +1095,35 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) ...@@ -1094,6 +1095,35 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
return 0; return 0;
} }
static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
{
struct sock *sk = d->session->sock->sk;
if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
return 1;
} else if (d->link_mode & RFCOMM_LM_AUTH) {
if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
return 1;
}
return 0;
}
static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p", d);
rfcomm_send_ua(d->session, d->dlci);
rfcomm_dlc_lock(d);
d->state = BT_CONNECTED;
d->state_change(d, 0);
rfcomm_dlc_unlock(d);
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
}
static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
{ {
struct rfcomm_dlc *d; struct rfcomm_dlc *d;
...@@ -1116,14 +1146,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) ...@@ -1116,14 +1146,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
if (d) { if (d) {
if (d->state == BT_OPEN) { if (d->state == BT_OPEN) {
/* DLC was previously opened by PN request */ /* DLC was previously opened by PN request */
rfcomm_send_ua(s, dlci); if (rfcomm_check_link_mode(d)) {
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
rfcomm_dlc_lock(d); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
d->state = BT_CONNECTED; return 0;
d->state_change(d, 0); }
rfcomm_dlc_unlock(d);
rfcomm_send_msc(s, 1, dlci, d->v24_sig); rfcomm_dlc_accept(d);
} }
return 0; return 0;
} }
...@@ -1135,14 +1164,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) ...@@ -1135,14 +1164,13 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
d->addr = __addr(s->initiator, dlci); d->addr = __addr(s->initiator, dlci);
rfcomm_dlc_link(s, d); rfcomm_dlc_link(s, d);
rfcomm_send_ua(s, dlci); if (rfcomm_check_link_mode(d)) {
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
rfcomm_dlc_lock(d); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
d->state = BT_CONNECTED; return 0;
d->state_change(d, 0); }
rfcomm_dlc_unlock(d);
rfcomm_send_msc(s, 1, dlci, d->v24_sig); rfcomm_dlc_accept(d);
} else { } else {
rfcomm_send_dm(s, dlci); rfcomm_send_dm(s, dlci);
} }
...@@ -1605,11 +1633,27 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) ...@@ -1605,11 +1633,27 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
list_for_each_safe(p, n, &s->dlcs) { list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list); d = list_entry(p, struct rfcomm_dlc, list);
if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
__rfcomm_dlc_close(d, ETIMEDOUT); __rfcomm_dlc_close(d, ETIMEDOUT);
continue; continue;
} }
if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) {
rfcomm_dlc_clear_timer(d);
rfcomm_dlc_accept(d);
if (d->link_mode & RFCOMM_LM_SECURE) {
struct sock *sk = s->sock->sk;
hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
}
continue;
} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
rfcomm_dlc_clear_timer(d);
rfcomm_send_dm(s, d->dlci);
__rfcomm_dlc_close(d, ECONNREFUSED);
continue;
}
if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
continue; continue;
...@@ -1842,6 +1886,77 @@ static int rfcomm_run(void *unused) ...@@ -1842,6 +1886,77 @@ static int rfcomm_run(void *unused)
return 0; return 0;
} }
static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
{
struct rfcomm_session *s;
struct rfcomm_dlc *d;
struct list_head *p, *n;
BT_DBG("conn %p status 0x%02x", conn, status);
s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
if (!s)
return;
rfcomm_session_hold(s);
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE))
continue;
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
continue;
if (!status)
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
else
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
}
rfcomm_session_put(s);
rfcomm_schedule(RFCOMM_SCHED_AUTH);
}
static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
{
struct rfcomm_session *s;
struct rfcomm_dlc *d;
struct list_head *p, *n;
BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
if (!s)
return;
rfcomm_session_hold(s);
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
continue;
if (!status && encrypt)
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
else
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
}
rfcomm_session_put(s);
rfcomm_schedule(RFCOMM_SCHED_AUTH);
}
static struct hci_cb rfcomm_cb = {
.name = "RFCOMM",
.auth_cfm = rfcomm_auth_cfm,
.encrypt_cfm = rfcomm_encrypt_cfm
};
/* ---- Proc fs support ---- */ /* ---- Proc fs support ---- */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos) static void *rfcomm_seq_start(struct seq_file *seq, loff_t *pos)
...@@ -1959,6 +2074,8 @@ static int __init rfcomm_init(void) ...@@ -1959,6 +2074,8 @@ static int __init rfcomm_init(void)
{ {
l2cap_load(); l2cap_load();
hci_register_cb(&rfcomm_cb);
kernel_thread(rfcomm_run, NULL, CLONE_KERNEL); kernel_thread(rfcomm_run, NULL, CLONE_KERNEL);
BT_INFO("RFCOMM ver %s", VERSION); BT_INFO("RFCOMM ver %s", VERSION);
...@@ -1976,6 +2093,8 @@ static int __init rfcomm_init(void) ...@@ -1976,6 +2093,8 @@ static int __init rfcomm_init(void)
static void __exit rfcomm_exit(void) static void __exit rfcomm_exit(void)
{ {
hci_unregister_cb(&rfcomm_cb);
/* Terminate working thread. /* Terminate working thread.
* ie. Set terminate flag and wake it up */ * ie. Set terminate flag and wake it up */
atomic_inc(&terminate); atomic_inc(&terminate);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment