summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-09-14 02:09:53 +0200
committerMarcel Holtmann <marcel@holtmann.org>2008-09-14 02:09:53 +0200
commit6ff238c6397298f2d241a280bd19feb2fa0e7e7b (patch)
tree2876073efcbebb86a1cab2cf046545a117648214
parente395a272e9277dd10cc8b30011aea937774e99c3 (diff)
parentdf041f69a2e7ecb8b3047e9296fd57df60b35757 (diff)
Merge branch 'encrypt' of git://gitorious.org/bluez/cktakahasis-clone
-rw-r--r--common/glib-helper.c187
-rw-r--r--common/glib-helper.h4
-rw-r--r--input/device.c214
3 files changed, 281 insertions, 124 deletions
diff --git a/common/glib-helper.c b/common/glib-helper.c
index 82162685..3ccadbcf 100644
--- a/common/glib-helper.c
+++ b/common/glib-helper.c
@@ -32,6 +32,8 @@
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/sco.h>
@@ -46,6 +48,13 @@ typedef int (*resolver_t) (int fd, char *src, char *dst);
typedef BtIOError (*connect_t) (BtIO *io, BtIOFunc func);
typedef BtIOError (*listen_t) (BtIO *io, BtIOFunc func);
+struct hci_cmd_data {
+ bt_hci_result_t cb;
+ uint16_t handle;
+ uint16_t ocf;
+ gpointer caller_data;
+};
+
int set_nonblocking(int fd)
{
long arg;
@@ -1005,6 +1014,184 @@ static BtIOError sco_listen(BtIO *io, BtIOFunc func)
return BT_IO_SUCCESS;
}
+static gboolean hci_event_watch(GIOChannel *io,
+ GIOCondition cond, gpointer user_data)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *body;
+ struct hci_cmd_data *cmd = user_data;
+ evt_cmd_status *evt_status;
+ evt_auth_complete *evt_auth;
+ evt_encrypt_change *evt_enc;
+ hci_event_hdr *hdr;
+ set_conn_encrypt_cp cp;
+ int dd;
+ uint16_t ocf;
+ uint8_t status = HCI_OE_POWER_OFF;
+
+ if (cond & G_IO_NVAL) {
+ cmd->cb(status, cmd->caller_data);
+ return FALSE;
+ }
+
+ if (cond & (G_IO_ERR | G_IO_HUP))
+ goto failed;
+
+ dd = g_io_channel_unix_get_fd(io);
+
+ if (read(dd, buf, sizeof(buf)) < 0)
+ goto failed;
+
+ hdr = (hci_event_hdr *) (buf + 1);
+ body = buf + (1 + HCI_EVENT_HDR_SIZE);
+
+ switch (hdr->evt) {
+ case EVT_CMD_STATUS:
+ evt_status = (evt_cmd_status *) body;
+ ocf = cmd_opcode_ocf(evt_status->opcode);
+ if (ocf != cmd->ocf)
+ return TRUE;
+ switch (ocf) {
+ case OCF_AUTH_REQUESTED:
+ case OCF_SET_CONN_ENCRYPT:
+ if (evt_status->status != 0) {
+ /* Baseband rejected command */
+ status = evt_status->status;
+ goto failed;
+ }
+ break;
+ default:
+ return TRUE;
+ }
+ /* Wait for the next event */
+ return TRUE;
+ case EVT_AUTH_COMPLETE:
+ evt_auth = (evt_auth_complete *) body;
+ if (evt_auth->handle != cmd->handle) {
+ /* Skipping */
+ return TRUE;
+ }
+
+ if (evt_auth->status != 0x00) {
+ status = evt_auth->status;
+ /* Abort encryption */
+ goto failed;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cmd->handle;
+ cp.encrypt = 1;
+
+ cmd->ocf = OCF_SET_CONN_ENCRYPT;
+
+ if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
+ SET_CONN_ENCRYPT_CP_SIZE, &cp) < 0) {
+ status = HCI_COMMAND_DISALLOWED;
+ goto failed;
+ }
+ /* Wait for encrypt change event */
+ return TRUE;
+ case EVT_ENCRYPT_CHANGE:
+ evt_enc = (evt_encrypt_change *) body;
+ if (evt_enc->handle != cmd->handle)
+ return TRUE;
+
+ /* Procedure finished: reporting status */
+ status = evt_enc->status;
+ break;
+ default:
+ /* Skipping */
+ return TRUE;
+ }
+
+failed:
+ cmd->cb(status, cmd->caller_data);
+ g_io_channel_close(io);
+
+ return FALSE;
+}
+
+int bt_acl_encrypt(const bdaddr_t *src, const bdaddr_t *dst,
+ bt_hci_result_t cb, gpointer user_data)
+{
+ GIOChannel *io;
+ struct hci_cmd_data *cmd;
+ struct hci_conn_info_req *cr;
+ auth_requested_cp cp;
+ struct hci_filter nf;
+ int dd, dev_id, err;
+ char src_addr[18];
+ uint32_t link_mode;
+ uint16_t handle;
+
+ ba2str(src, src_addr);
+ dev_id = hci_devid(src_addr);
+ if (dev_id < 0)
+ return -errno;
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return -errno;
+
+ cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+ cr->type = ACL_LINK;
+ bacpy(&cr->bdaddr, dst);
+
+ err = ioctl(dd, HCIGETCONNINFO, cr);
+ link_mode = cr->conn_info->link_mode;
+ handle = cr->conn_info->handle;
+ g_free(cr);
+
+ if (err < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ if (link_mode & HCI_LM_ENCRYPT) {
+ /* Already encrypted */
+ err = EALREADY;
+ goto failed;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(handle);
+
+ if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+ AUTH_REQUESTED_CP_SIZE, &cp) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ cmd = g_new0(struct hci_cmd_data, 1);
+ cmd->handle = handle;
+ cmd->ocf = OCF_AUTH_REQUESTED;
+ cmd->cb = cb;
+ cmd->caller_data = user_data;
+
+ hci_filter_clear(&nf);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+ hci_filter_set_event(EVT_CMD_STATUS, &nf);
+ hci_filter_set_event(EVT_AUTH_COMPLETE, &nf);
+ hci_filter_set_event(EVT_ENCRYPT_CHANGE, &nf);
+
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ io = g_io_channel_unix_new(dd);
+ g_io_channel_set_close_on_unref(io, FALSE);
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
+ hci_event_watch, cmd, g_free);
+ g_io_channel_unref(io);
+
+ return 0;
+
+failed:
+ close(dd);
+
+ return -err;
+}
static int create_io_context(struct io_context **io_ctxt, BtIOFunc func,
gpointer cb, gpointer resolver, gpointer user_data)
diff --git a/common/glib-helper.h b/common/glib-helper.h
index 8e12bf19..ee63b2d7 100644
--- a/common/glib-helper.h
+++ b/common/glib-helper.h
@@ -27,6 +27,7 @@ typedef void (*bt_io_callback_t) (GIOChannel *io, int err, const bdaddr_t *src,
const bdaddr_t *dst, gpointer user_data);
typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
typedef void (*bt_destroy_t) (gpointer user_data);
+typedef void (*bt_hci_result_t) (uint8_t status, gpointer user_data);
int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst,
bt_callback_t cb, void *user_data, bt_destroy_t destroy);
@@ -59,6 +60,9 @@ int bt_sco_connect(const bdaddr_t *src, const bdaddr_t *dst,
GIOChannel *bt_sco_listen(const bdaddr_t *src, uint16_t mtu,
bt_io_callback_t cb, void *user_data);
+int bt_acl_encrypt(const bdaddr_t *src, const bdaddr_t *dst,
+ bt_hci_result_t cb, gpointer user_data);
+
/* Experiemental bt_io API */
typedef struct bt_io BtIO;
diff --git a/input/device.c b/input/device.c
index 1726ed97..4d211d93 100644
--- a/input/device.c
+++ b/input/device.c
@@ -51,6 +51,8 @@
#include "uinput.h"
#include "../src/storage.h"
+#include "../src/manager.h"
+#include "adapter.h"
#include "device.h"
#include "error.h"
@@ -364,7 +366,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,
path = dbus_message_get_path(iconn->pending_connect);
g_dbus_emit_signal(idev->conn, path,
INPUT_DEVICE_INTERFACE, "Connected",
- DBUS_TYPE_STRING, &iconn->uuid,
DBUS_TYPE_INVALID);
dbus_message_unref(iconn->pending_connect);
@@ -405,7 +406,6 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
g_dbus_emit_signal(idev->conn, idev->path,
INPUT_DEVICE_INTERFACE, "Disconnected",
- DBUS_TYPE_STRING, &iconn->uuid,
DBUS_TYPE_INVALID);
g_source_remove(iconn->ctrl_watch);
@@ -430,8 +430,9 @@ static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
if (cond & (G_IO_HUP | G_IO_ERR))
g_io_channel_close(chan);
- g_dbus_emit_signal(idev->conn, idev->path, INPUT_DEVICE_INTERFACE,
- "Disconnected", DBUS_TYPE_INVALID);
+ g_dbus_emit_signal(idev->conn, idev->path,
+ INPUT_DEVICE_INTERFACE, "Disconnected",
+ DBUS_TYPE_INVALID);
g_source_remove(iconn->intr_watch);
iconn->intr_watch = 0;
@@ -472,86 +473,6 @@ static int fake_hid_disconnect(struct input_conn *iconn)
return fhid->disconnect(iconn->fake);
}
-static int encrypt_link(const char *src_addr, const char *dst_addr)
-{
- char filename[PATH_MAX + 1];
- struct hci_conn_info_req *cr;
- int dd, err, dev_id;
- auth_requested_cp cp;
- char *str;
-
- create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "linkkeys");
-
- str = textfile_get(filename, dst_addr);
- if (!str) {
- error("Encryption link key not found");
- return -ENOKEY;
- }
-
- free(str);
-
- cr = g_try_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
- if (!cr)
- return -ENOMEM;
-
- dev_id = hci_devid(src_addr);
- if (dev_id < 0) {
- g_free(cr);
- return -errno;
- }
-
- dd = hci_open_dev(dev_id);
- if (dd < 0) {
- g_free(cr);
- return -errno;
- }
-
- str2ba(dst_addr, &cr->bdaddr);
- cr->type = ACL_LINK;
-
- if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0)
- goto fail;
-
- if (cr->conn_info->link_mode & HCI_LM_ENCRYPT) {
- /* Already encrypted */
- goto done;
- }
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(cr->conn_info->handle);
-
- if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
- sizeof(cp), &cp) < 0) {
- error("Link authentication failed: %s (%d)",
- strerror(errno), errno);
- goto fail;
- }
-
-#if 0
- /* FIXME: This needs to be done after auth completed */
- if (hci_encrypt_link(dd, handle, 1, 5000) < 0) {
- error("Link encryption failed: %s (%d)",
- strerror(errno), errno);
- goto fail;
- }
-#endif
-
-done:
- g_free(cr);
-
- hci_close_dev(dd);
-
- return 0;
-
-fail:
- g_free(cr);
-
- err = errno;
- hci_close_dev(dd);
-
- return -err;
-}
-
static void epox_endian_quirk(unsigned char *data, int size)
{
/* USAGE_PAGE (Keyboard) 05 07
@@ -634,21 +555,64 @@ static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req)
}
}
-static int hidp_connadd(const bdaddr_t *src, const bdaddr_t *dst, int ctrl_sk,
+static int ioctl_connadd(struct hidp_connadd_req *req)
+{
+ int ctl, err = 0;
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+ if (ctl < 0)
+ return -errno;
+
+ if (ioctl(ctl, HIDPCONNADD, req) < 0)
+ err = errno;
+
+ close(ctl);
+
+ return -err;
+}
+
+static void encrypt_completed(uint8_t status, gpointer user_data)
+{
+ struct hidp_connadd_req *req = user_data;
+ int err;
+
+ if (status) {
+ error("Encryption failed: %s(0x%x)",
+ strerror(bt_error(status)), status);
+ goto failed;
+ }
+
+ err = ioctl_connadd(req);
+ if (err == 0)
+ goto cleanup;
+
+ error("ioctl_connadd(): %s(%d)", strerror(-err), -err);
+failed:
+ close(req->intr_sock);
+ close(req->ctrl_sock);
+
+cleanup:
+ if (req->rd_data)
+ free(req->rd_data);
+
+ g_free(req);
+}
+
+static int hidp_add_connection(const bdaddr_t *src, const bdaddr_t *dst, int ctrl_sk,
int intr_sk, int timeout, const char *name, const uint32_t handle)
{
- struct hidp_connadd_req req;
+ struct hidp_connadd_req *req;
struct fake_hid *fake_hid;
struct fake_input *fake;
sdp_record_t *rec;
char src_addr[18], dst_addr[18];
- int ctl, err;
+ int err;
- memset(&req, 0, sizeof(req));
- req.ctrl_sock = ctrl_sk;
- req.intr_sock = intr_sk;
- req.flags = 0;
- req.idle_to = timeout;
+ req = g_new0(struct hidp_connadd_req, 1);
+ req->ctrl_sock = ctrl_sk;
+ req->intr_sock = intr_sk;
+ req->flags = 0;
+ req->idle_to = timeout;
ba2str(src, src_addr);
ba2str(dst, dst_addr);
@@ -660,49 +624,49 @@ static int hidp_connadd(const bdaddr_t *src, const bdaddr_t *dst, int ctrl_sk,
goto cleanup;
}
- extract_hid_record(rec, &req);
+ extract_hid_record(rec, req);
sdp_record_free(rec);
- fake_hid = get_fake_hid(req.vendor, req.product);
+ fake_hid = get_fake_hid(req->vendor, req->product);
if (fake_hid) {
fake = g_new0(struct fake_input, 1);
fake->connect = fake_hid_connect;
fake->disconnect = fake_hid_disconnect;
fake->priv = fake_hid;
- return fake_hid_connadd(fake, intr_sk, fake_hid);
- }
-
- ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
- if (ctl < 0) {
- error("Can't open HIDP interface");
- return -errno;
+ err = fake_hid_connadd(fake, intr_sk, fake_hid);
+ goto cleanup;
}
- if (req.subclass & 0x40) {
- err = encrypt_link(src_addr, dst_addr);
- if (err < 0 && err != -EACCES)
+ if (name)
+ strncpy(req->name, name, 128);
+
+ /* Encryption is mandatory for keyboards */
+ if (req->subclass & 0x40) {
+ err = bt_acl_encrypt(src, dst, encrypt_completed, req);
+ if (err == 0) {
+ /* Waiting async encryption */
+ return 0;
+ } else if (err != -EALREADY) {
+ error("bt_acl_encrypt(): %s(%d)", strerror(-err), -err);
goto cleanup;
- }
+ }
- if (name)
- strncpy(req.name, name, 128);
+ /* Link already encrypted - reset error */
+ err = 0;
+ }
- if (req.vendor == 0x054c && req.product == 0x0268) {
+ /* Encryption not required */
+ if (req->vendor == 0x054c && req->product == 0x0268) {
unsigned char buf[] = { 0x53, 0xf4, 0x42, 0x03, 0x00, 0x00 };
err = write(ctrl_sk, buf, sizeof(buf));
}
- err = ioctl(ctl, HIDPCONNADD, &req);
- if (err < 0) {
- close(ctl);
- goto cleanup;
- }
-
- info("New input device %s (%s)", dst_addr, req.name);
+ err = ioctl_connadd(req);
cleanup:
- if (req.rd_data)
- free(req.rd_data);
+ if (req->rd_data)
+ free(req->rd_data);
+ g_free(req);
return err;
}
@@ -720,9 +684,10 @@ static void interrupt_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,
}
iconn->intr_sk = g_io_channel_unix_get_fd(chan);
- err = hidp_connadd(&idev->src, &idev->dst,
+ err = hidp_add_connection(&idev->src, &idev->dst,
iconn->ctrl_sk, iconn->intr_sk,
iconn->timeout, idev->name, idev->handle);
+
if (err < 0)
goto failed;
@@ -730,7 +695,6 @@ static void interrupt_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,
iconn->ctrl_watch = create_watch(iconn->ctrl_sk, ctrl_watch_cb, iconn);
g_dbus_emit_signal(idev->conn, idev->path,
INPUT_DEVICE_INTERFACE, "Connected",
- DBUS_TYPE_STRING, &iconn->uuid,
DBUS_TYPE_INVALID);
/* Replying to the requestor */
@@ -755,6 +719,7 @@ static void control_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,
{
struct input_conn *iconn = user_data;
struct input_device *idev = iconn->idev;
+ DBusMessage *reply;
if (err < 0) {
error("connect(): %s (%d)", strerror(-err), -err);
@@ -776,7 +741,8 @@ static void control_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,
failed:
iconn->ctrl_sk = -1;
- connection_attempt_failed(iconn->pending_connect, -err);
+ reply = connection_attempt_failed(iconn->pending_connect, -err);
+ g_dbus_send_message(idev->conn, reply);
dbus_message_unref(iconn->pending_connect);
iconn->pending_connect = NULL;
}
@@ -1023,8 +989,8 @@ static GDBusMethodTable device_methods[] = {
};
static GDBusSignalTable device_signals[] = {
- { "Connected", "ss" },
- { "Disconnected", "s" },
+ { "Connected", "" },
+ { "Disconnected", "" },
{ }
};
@@ -1237,7 +1203,7 @@ int input_device_connadd(const bdaddr_t *src, const bdaddr_t *dst)
if (!iconn)
return -ENOENT;
- err = hidp_connadd(src, dst, iconn->ctrl_sk, iconn->intr_sk,
+ err = hidp_add_connection(src, dst, iconn->ctrl_sk, iconn->intr_sk,
iconn->timeout, idev->name, idev->handle);
if (err < 0)
goto error;
@@ -1246,7 +1212,6 @@ int input_device_connadd(const bdaddr_t *src, const bdaddr_t *dst)
iconn->ctrl_watch = create_watch(iconn->ctrl_sk, ctrl_watch_cb, iconn);
g_dbus_emit_signal(idev->conn, idev->path,
INPUT_DEVICE_INTERFACE, "Connected",
- DBUS_TYPE_STRING, &iconn->uuid,
DBUS_TYPE_INVALID);
return 0;
@@ -1255,5 +1220,6 @@ error:
close(iconn->intr_sk);
iconn->ctrl_sk = -1;
iconn->intr_sk = -1;
+
return err;
}