diff options
| author | Brian Gix <bgix@codeaurora.org> | 2011-12-21 19:12:12 -0500 |
|---|---|---|
| committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-12-22 11:18:59 -0500 |
| commit | 2b64d153a0cc9d2b60e47be013cde8490f16e0a5 (patch) | |
| tree | ea075313e9f03379ee1313ca230b07dadd937dad | |
| parent | 371fd83563252f550ce59476a7366d0b5171d316 (diff) | |
Bluetooth: Add MITM mechanism to LE-SMP
To achive Man-In-The-Middle (MITM) level security with Low Energy,
we have to enable User Passkey Comparison. This commit modifies the
hard-coded JUST-WORKS pairing mechanism to support query via the MGMT
interface of Passkey comparison and User Confirmation.
Signed-off-by: Brian Gix <bgix@codeaurora.org>
Acked-by: Marcel Holtmann<marcel@holtmann.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 1 | ||||
| -rw-r--r-- | include/net/bluetooth/smp.h | 6 | ||||
| -rw-r--r-- | net/bluetooth/smp.c | 226 |
3 files changed, 211 insertions, 22 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5ce73dbaf604..4ff08d61eea5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
| @@ -310,6 +310,7 @@ struct hci_conn { | |||
| 310 | struct hci_dev *hdev; | 310 | struct hci_dev *hdev; |
| 311 | void *l2cap_data; | 311 | void *l2cap_data; |
| 312 | void *sco_data; | 312 | void *sco_data; |
| 313 | void *smp_conn; | ||
| 313 | 314 | ||
| 314 | struct hci_conn *link; | 315 | struct hci_conn *link; |
| 315 | 316 | ||
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 15b97d549441..aeaf5fa2b9f1 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h | |||
| @@ -115,6 +115,10 @@ struct smp_cmd_security_req { | |||
| 115 | #define SMP_MIN_ENC_KEY_SIZE 7 | 115 | #define SMP_MIN_ENC_KEY_SIZE 7 |
| 116 | #define SMP_MAX_ENC_KEY_SIZE 16 | 116 | #define SMP_MAX_ENC_KEY_SIZE 16 |
| 117 | 117 | ||
| 118 | #define SMP_FLAG_TK_VALID 1 | ||
| 119 | #define SMP_FLAG_CFM_PENDING 2 | ||
| 120 | #define SMP_FLAG_MITM_AUTH 3 | ||
| 121 | |||
| 118 | struct smp_chan { | 122 | struct smp_chan { |
| 119 | struct l2cap_conn *conn; | 123 | struct l2cap_conn *conn; |
| 120 | u8 preq[7]; /* SMP Pairing Request */ | 124 | u8 preq[7]; /* SMP Pairing Request */ |
| @@ -124,6 +128,7 @@ struct smp_chan { | |||
| 124 | u8 pcnf[16]; /* SMP Pairing Confirm */ | 128 | u8 pcnf[16]; /* SMP Pairing Confirm */ |
| 125 | u8 tk[16]; /* SMP Temporary Key */ | 129 | u8 tk[16]; /* SMP Temporary Key */ |
| 126 | u8 smp_key_size; | 130 | u8 smp_key_size; |
| 131 | unsigned long smp_flags; | ||
| 127 | struct crypto_blkcipher *tfm; | 132 | struct crypto_blkcipher *tfm; |
| 128 | struct work_struct confirm; | 133 | struct work_struct confirm; |
| 129 | struct work_struct random; | 134 | struct work_struct random; |
| @@ -134,6 +139,7 @@ struct smp_chan { | |||
| 134 | int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); | 139 | int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); |
| 135 | int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); | 140 | int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); |
| 136 | int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); | 141 | int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); |
| 142 | int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); | ||
| 137 | 143 | ||
| 138 | void smp_chan_destroy(struct l2cap_conn *conn); | 144 | void smp_chan_destroy(struct l2cap_conn *conn); |
| 139 | 145 | ||
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0ee2905a6179..9fea4bfd0eb5 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <net/bluetooth/bluetooth.h> | 23 | #include <net/bluetooth/bluetooth.h> |
| 24 | #include <net/bluetooth/hci_core.h> | 24 | #include <net/bluetooth/hci_core.h> |
| 25 | #include <net/bluetooth/l2cap.h> | 25 | #include <net/bluetooth/l2cap.h> |
| 26 | #include <net/bluetooth/mgmt.h> | ||
| 26 | #include <net/bluetooth/smp.h> | 27 | #include <net/bluetooth/smp.h> |
| 27 | #include <linux/crypto.h> | 28 | #include <linux/crypto.h> |
| 28 | #include <linux/scatterlist.h> | 29 | #include <linux/scatterlist.h> |
| @@ -189,24 +190,45 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) | |||
| 189 | msecs_to_jiffies(SMP_TIMEOUT)); | 190 | msecs_to_jiffies(SMP_TIMEOUT)); |
| 190 | } | 191 | } |
| 191 | 192 | ||
| 193 | static __u8 authreq_to_seclevel(__u8 authreq) | ||
| 194 | { | ||
| 195 | if (authreq & SMP_AUTH_MITM) | ||
| 196 | return BT_SECURITY_HIGH; | ||
| 197 | else | ||
| 198 | return BT_SECURITY_MEDIUM; | ||
| 199 | } | ||
| 200 | |||
| 201 | static __u8 seclevel_to_authreq(__u8 sec_level) | ||
| 202 | { | ||
| 203 | switch (sec_level) { | ||
| 204 | case BT_SECURITY_HIGH: | ||
| 205 | return SMP_AUTH_MITM | SMP_AUTH_BONDING; | ||
| 206 | case BT_SECURITY_MEDIUM: | ||
| 207 | return SMP_AUTH_BONDING; | ||
| 208 | default: | ||
| 209 | return SMP_AUTH_NONE; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 192 | static void build_pairing_cmd(struct l2cap_conn *conn, | 213 | static void build_pairing_cmd(struct l2cap_conn *conn, |
| 193 | struct smp_cmd_pairing *req, | 214 | struct smp_cmd_pairing *req, |
| 194 | struct smp_cmd_pairing *rsp, | 215 | struct smp_cmd_pairing *rsp, |
| 195 | __u8 authreq) | 216 | __u8 authreq) |
| 196 | { | 217 | { |
| 197 | u8 dist_keys; | 218 | u8 dist_keys = 0; |
| 198 | 219 | ||
| 199 | dist_keys = 0; | ||
| 200 | if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) { | 220 | if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) { |
| 201 | dist_keys = SMP_DIST_ENC_KEY; | 221 | dist_keys = SMP_DIST_ENC_KEY; |
| 202 | authreq |= SMP_AUTH_BONDING; | 222 | authreq |= SMP_AUTH_BONDING; |
| 223 | } else { | ||
| 224 | authreq &= ~SMP_AUTH_BONDING; | ||
| 203 | } | 225 | } |
| 204 | 226 | ||
| 205 | if (rsp == NULL) { | 227 | if (rsp == NULL) { |
| 206 | req->io_capability = conn->hcon->io_capability; | 228 | req->io_capability = conn->hcon->io_capability; |
| 207 | req->oob_flag = SMP_OOB_NOT_PRESENT; | 229 | req->oob_flag = SMP_OOB_NOT_PRESENT; |
| 208 | req->max_key_size = SMP_MAX_ENC_KEY_SIZE; | 230 | req->max_key_size = SMP_MAX_ENC_KEY_SIZE; |
| 209 | req->init_key_dist = dist_keys; | 231 | req->init_key_dist = 0; |
| 210 | req->resp_key_dist = dist_keys; | 232 | req->resp_key_dist = dist_keys; |
| 211 | req->auth_req = authreq; | 233 | req->auth_req = authreq; |
| 212 | return; | 234 | return; |
| @@ -215,7 +237,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, | |||
| 215 | rsp->io_capability = conn->hcon->io_capability; | 237 | rsp->io_capability = conn->hcon->io_capability; |
| 216 | rsp->oob_flag = SMP_OOB_NOT_PRESENT; | 238 | rsp->oob_flag = SMP_OOB_NOT_PRESENT; |
| 217 | rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; | 239 | rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; |
| 218 | rsp->init_key_dist = req->init_key_dist & dist_keys; | 240 | rsp->init_key_dist = 0; |
| 219 | rsp->resp_key_dist = req->resp_key_dist & dist_keys; | 241 | rsp->resp_key_dist = req->resp_key_dist & dist_keys; |
| 220 | rsp->auth_req = authreq; | 242 | rsp->auth_req = authreq; |
| 221 | } | 243 | } |
| @@ -245,6 +267,95 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) | |||
| 245 | smp_chan_destroy(conn); | 267 | smp_chan_destroy(conn); |
| 246 | } | 268 | } |
| 247 | 269 | ||
| 270 | #define JUST_WORKS 0x00 | ||
| 271 | #define JUST_CFM 0x01 | ||
| 272 | #define REQ_PASSKEY 0x02 | ||
| 273 | #define CFM_PASSKEY 0x03 | ||
| 274 | #define REQ_OOB 0x04 | ||
| 275 | #define OVERLAP 0xFF | ||
| 276 | |||
| 277 | static const u8 gen_method[5][5] = { | ||
| 278 | { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, | ||
| 279 | { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, | ||
| 280 | { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, | ||
| 281 | { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, | ||
| 282 | { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, | ||
| 283 | }; | ||
| 284 | |||
| 285 | static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, | ||
| 286 | u8 local_io, u8 remote_io) | ||
| 287 | { | ||
| 288 | struct hci_conn *hcon = conn->hcon; | ||
| 289 | struct smp_chan *smp = conn->smp_chan; | ||
| 290 | u8 method; | ||
| 291 | u32 passkey = 0; | ||
| 292 | int ret = 0; | ||
| 293 | |||
| 294 | /* Initialize key for JUST WORKS */ | ||
| 295 | memset(smp->tk, 0, sizeof(smp->tk)); | ||
| 296 | clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); | ||
| 297 | |||
| 298 | BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); | ||
| 299 | |||
| 300 | /* If neither side wants MITM, use JUST WORKS */ | ||
| 301 | /* If either side has unknown io_caps, use JUST WORKS */ | ||
| 302 | /* Otherwise, look up method from the table */ | ||
| 303 | if (!(auth & SMP_AUTH_MITM) || | ||
| 304 | local_io > SMP_IO_KEYBOARD_DISPLAY || | ||
| 305 | remote_io > SMP_IO_KEYBOARD_DISPLAY) | ||
| 306 | method = JUST_WORKS; | ||
| 307 | else | ||
| 308 | method = gen_method[local_io][remote_io]; | ||
| 309 | |||
| 310 | /* If not bonding, don't ask user to confirm a Zero TK */ | ||
| 311 | if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) | ||
| 312 | method = JUST_WORKS; | ||
| 313 | |||
| 314 | /* If Just Works, Continue with Zero TK */ | ||
| 315 | if (method == JUST_WORKS) { | ||
| 316 | set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); | ||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Not Just Works/Confirm results in MITM Authentication */ | ||
| 321 | if (method != JUST_CFM) | ||
| 322 | set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags); | ||
| 323 | |||
| 324 | /* If both devices have Keyoard-Display I/O, the master | ||
| 325 | * Confirms and the slave Enters the passkey. | ||
| 326 | |||
