Commit 2af6b9d5 authored by Marcel Holtmann's avatar Marcel Holtmann

Bluetooth: Replace L2CAP link mode with security level

Change the L2CAP internals to use the new security levels and remove
the link mode details.
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 8c1b2355
...@@ -237,8 +237,9 @@ struct l2cap_pinfo { ...@@ -237,8 +237,9 @@ struct l2cap_pinfo {
__u16 imtu; __u16 imtu;
__u16 omtu; __u16 omtu;
__u16 flush_to; __u16 flush_to;
__u8 sec_level;
__u32 link_mode; __u8 role_switch;
__u8 force_reliable;
__u8 conf_req[64]; __u8 conf_req[64];
__u8 conf_len; __u8 conf_len;
......
...@@ -78,8 +78,7 @@ static void l2cap_sock_timeout(unsigned long arg) ...@@ -78,8 +78,7 @@ static void l2cap_sock_timeout(unsigned long arg)
bh_lock_sock(sk); bh_lock_sock(sk);
if (sk->sk_state == BT_CONNECT && if (sk->sk_state == BT_CONNECT &&
(l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH | l2cap_pi(sk)->sec_level != BT_SECURITY_SDP)
L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
reason = ECONNREFUSED; reason = ECONNREFUSED;
else else
reason = ETIMEDOUT; reason = ETIMEDOUT;
...@@ -259,23 +258,11 @@ static void l2cap_chan_del(struct sock *sk, int err) ...@@ -259,23 +258,11 @@ static void l2cap_chan_del(struct sock *sk, int err)
} }
/* Service level security */ /* Service level security */
static inline int l2cap_check_link_mode(struct sock *sk) static inline int l2cap_check_security(struct sock *sk)
{ {
struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_conn *conn = l2cap_pi(sk)->conn;
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level);
return hci_conn_security(conn->hcon, BT_SECURITY_HIGH);
if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)
return hci_conn_security(conn->hcon, BT_SECURITY_MEDIUM);
if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
return hci_conn_security(conn->hcon, BT_SECURITY_LOW);
if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
return hci_conn_security(conn->hcon, BT_SECURITY_SDP);
return 1;
} }
static inline u8 l2cap_get_ident(struct l2cap_conn *conn) static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
...@@ -317,7 +304,7 @@ static void l2cap_do_start(struct sock *sk) ...@@ -317,7 +304,7 @@ static void l2cap_do_start(struct sock *sk)
struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_conn *conn = l2cap_pi(sk)->conn;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
if (l2cap_check_link_mode(sk)) { if (l2cap_check_security(sk)) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
req.psm = l2cap_pi(sk)->psm; req.psm = l2cap_pi(sk)->psm;
...@@ -361,7 +348,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -361,7 +348,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
} }
if (sk->sk_state == BT_CONNECT) { if (sk->sk_state == BT_CONNECT) {
if (l2cap_check_link_mode(sk)) { if (l2cap_check_security(sk)) {
struct l2cap_conn_req req; struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
req.psm = l2cap_pi(sk)->psm; req.psm = l2cap_pi(sk)->psm;
...@@ -376,7 +363,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -376,7 +363,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
if (l2cap_check_link_mode(sk)) { if (l2cap_check_security(sk)) {
if (bt_sk(sk)->defer_setup) { if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent; struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND); rsp.result = cpu_to_le16(L2CAP_CR_PEND);
...@@ -439,7 +426,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) ...@@ -439,7 +426,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
read_lock(&l->lock); read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) if (l2cap_pi(sk)->force_reliable)
sk->sk_err = err; sk->sk_err = err;
} }
...@@ -690,11 +677,15 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) ...@@ -690,11 +677,15 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
pi->imtu = l2cap_pi(parent)->imtu; pi->imtu = l2cap_pi(parent)->imtu;
pi->omtu = l2cap_pi(parent)->omtu; pi->omtu = l2cap_pi(parent)->omtu;
pi->link_mode = l2cap_pi(parent)->link_mode; pi->sec_level = l2cap_pi(parent)->sec_level;
pi->role_switch = l2cap_pi(parent)->role_switch;
pi->force_reliable = l2cap_pi(parent)->force_reliable;
} else { } else {
pi->imtu = L2CAP_DEFAULT_MTU; pi->imtu = L2CAP_DEFAULT_MTU;
pi->omtu = 0; pi->omtu = 0;
pi->link_mode = 0; pi->sec_level = BT_SECURITY_LOW;
pi->role_switch = 0;
pi->force_reliable = 0;
} }
/* Default config options */ /* Default config options */
...@@ -792,6 +783,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ ...@@ -792,6 +783,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
l2cap_pi(sk)->psm = la->l2_psm; l2cap_pi(sk)->psm = la->l2_psm;
l2cap_pi(sk)->sport = la->l2_psm; l2cap_pi(sk)->sport = la->l2_psm;
sk->sk_state = BT_BOUND; sk->sk_state = BT_BOUND;
if (btohs(la->l2_psm) == 0x0001)
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
} }
write_unlock_bh(&l2cap_sk_list.lock); write_unlock_bh(&l2cap_sk_list.lock);
...@@ -808,7 +802,6 @@ static int l2cap_do_connect(struct sock *sk) ...@@ -808,7 +802,6 @@ static int l2cap_do_connect(struct sock *sk)
struct l2cap_conn *conn; struct l2cap_conn *conn;
struct hci_conn *hcon; struct hci_conn *hcon;
struct hci_dev *hdev; struct hci_dev *hdev;
__u8 sec_level;
__u8 auth_type; __u8 auth_type;
int err = 0; int err = 0;
...@@ -821,37 +814,39 @@ static int l2cap_do_connect(struct sock *sk) ...@@ -821,37 +814,39 @@ static int l2cap_do_connect(struct sock *sk)
err = -ENOMEM; err = -ENOMEM;
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE)
sec_level = BT_SECURITY_HIGH;
else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
sec_level = BT_SECURITY_SDP;
else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)
sec_level = BT_SECURITY_MEDIUM;
else
sec_level = BT_SECURITY_LOW;
if (sk->sk_type == SOCK_RAW) { if (sk->sk_type == SOCK_RAW) {
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) switch (l2cap_pi(sk)->sec_level) {
case BT_SECURITY_HIGH:
auth_type = HCI_AT_DEDICATED_BONDING_MITM; auth_type = HCI_AT_DEDICATED_BONDING_MITM;
else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) break;
case BT_SECURITY_MEDIUM:
auth_type = HCI_AT_DEDICATED_BONDING; auth_type = HCI_AT_DEDICATED_BONDING;
else break;
default:
auth_type = HCI_AT_NO_BONDING; auth_type = HCI_AT_NO_BONDING;
break;
}
} else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) { } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
auth_type = HCI_AT_NO_BONDING_MITM; auth_type = HCI_AT_NO_BONDING_MITM;
else else
auth_type = HCI_AT_NO_BONDING; auth_type = HCI_AT_NO_BONDING;
} else { } else {
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) switch (l2cap_pi(sk)->sec_level) {
case BT_SECURITY_HIGH:
auth_type = HCI_AT_GENERAL_BONDING_MITM; auth_type = HCI_AT_GENERAL_BONDING_MITM;
else if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) break;
case BT_SECURITY_MEDIUM:
auth_type = HCI_AT_GENERAL_BONDING; auth_type = HCI_AT_GENERAL_BONDING;
else break;
default:
auth_type = HCI_AT_NO_BONDING; auth_type = HCI_AT_NO_BONDING;
break;
}
} }
hcon = hci_connect(hdev, ACL_LINK, dst, sec_level, auth_type); hcon = hci_connect(hdev, ACL_LINK, dst,
l2cap_pi(sk)->sec_level, auth_type);
if (!hcon) if (!hcon)
goto done; goto done;
...@@ -1219,7 +1214,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us ...@@ -1219,7 +1214,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break; break;
} }
l2cap_pi(sk)->link_mode = opt; if (opt & L2CAP_LM_AUTH)
l2cap_pi(sk)->sec_level = BT_SECURITY_LOW;
if (opt & L2CAP_LM_ENCRYPT)
l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
if (opt & L2CAP_LM_SECURE)
l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH;
l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER);
l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE);
break; break;
default: default:
...@@ -1234,7 +1237,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us ...@@ -1234,7 +1237,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int err = 0; struct bt_security sec;
int len, err = 0;
u32 opt; u32 opt;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
...@@ -1245,6 +1249,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch ...@@ -1245,6 +1249,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
lock_sock(sk); lock_sock(sk);
switch (optname) { switch (optname) {
case BT_SECURITY:
sec.level = BT_SECURITY_LOW;
len = min_t(unsigned int, sizeof(sec), optlen);
if (copy_from_user((char *) &sec, optval, len)) {
err = -EFAULT;
break;
}
if (sec.level < BT_SECURITY_LOW ||
sec.level > BT_SECURITY_HIGH) {
err = -EINVAL;
break;
}
l2cap_pi(sk)->sec_level = sec.level;
break;
case BT_DEFER_SETUP: case BT_DEFER_SETUP:
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
err = -EINVAL; err = -EINVAL;
...@@ -1274,6 +1296,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us ...@@ -1274,6 +1296,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
struct l2cap_options opts; struct l2cap_options opts;
struct l2cap_conninfo cinfo; struct l2cap_conninfo cinfo;
int len, err = 0; int len, err = 0;
u32 opt;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
...@@ -1296,7 +1319,29 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us ...@@ -1296,7 +1319,29 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break; break;
case L2CAP_LM: case L2CAP_LM:
if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval)) switch (l2cap_pi(sk)->sec_level) {
case BT_SECURITY_LOW:
opt = L2CAP_LM_AUTH;
break;
case BT_SECURITY_MEDIUM:
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT;
break;
case BT_SECURITY_HIGH:
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
L2CAP_LM_SECURE;
break;
default:
opt = 0;
break;
}
if (l2cap_pi(sk)->role_switch)
opt |= L2CAP_LM_MASTER;
if (l2cap_pi(sk)->force_reliable)
opt |= L2CAP_LM_RELIABLE;
if (put_user(opt, (u32 __user *) optval))
err = -EFAULT; err = -EFAULT;
break; break;
...@@ -1329,6 +1374,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us ...@@ -1329,6 +1374,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct bt_security sec;
int len, err = 0; int len, err = 0;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
...@@ -1342,6 +1388,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch ...@@ -1342,6 +1388,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
lock_sock(sk); lock_sock(sk);
switch (optname) { switch (optname) {
case BT_SECURITY:
sec.level = l2cap_pi(sk)->sec_level;
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
err = -EFAULT;
break;
case BT_DEFER_SETUP: case BT_DEFER_SETUP:
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
err = -EINVAL; err = -EINVAL;
...@@ -1771,7 +1826,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -1771,7 +1826,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
l2cap_pi(sk)->ident = cmd->ident; l2cap_pi(sk)->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
if (l2cap_check_link_mode(sk)) { if (l2cap_check_security(sk)) {
if (bt_sk(sk)->defer_setup) { if (bt_sk(sk)->defer_setup) {
sk->sk_state = BT_CONNECT2; sk->sk_state = BT_CONNECT2;
result = L2CAP_CR_PEND; result = L2CAP_CR_PEND;
...@@ -2299,10 +2354,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) ...@@ -2299,10 +2354,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
continue; continue;
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); lm1 |= HCI_LM_ACCEPT;
if (l2cap_pi(sk)->role_switch)
lm1 |= HCI_LM_MASTER;
exact++; exact++;
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); lm2 |= HCI_LM_ACCEPT;
if (l2cap_pi(sk)->role_switch)
lm2 |= HCI_LM_MASTER;
}
} }
read_unlock(&l2cap_sk_list.lock); read_unlock(&l2cap_sk_list.lock);
...@@ -2361,7 +2421,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) ...@@ -2361,7 +2421,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
bh_lock_sock(sk); bh_lock_sock(sk);
if (!status && encrypt == 0x00 && if (!status && encrypt == 0x00 &&
(pi->link_mode & L2CAP_LM_SECURE) && pi->sec_level == BT_SECURITY_HIGH &&
(sk->sk_state == BT_CONNECTED || (sk->sk_state == BT_CONNECTED ||
sk->sk_state == BT_CONFIG)) { sk->sk_state == BT_CONFIG)) {
__l2cap_sock_close(sk, ECONNREFUSED); __l2cap_sock_close(sk, ECONNREFUSED);
...@@ -2510,10 +2570,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf) ...@@ -2510,10 +2570,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf)
sk_for_each(sk, node, &l2cap_sk_list.head) { sk_for_each(sk, node, &l2cap_sk_list.head) {
struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_pinfo *pi = l2cap_pi(sk);
str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n", str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid, sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid,
pi->imtu, pi->omtu, pi->link_mode); pi->imtu, pi->omtu, pi->sec_level);
} }
read_unlock_bh(&l2cap_sk_list.lock); read_unlock_bh(&l2cap_sk_list.lock);
......
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