diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2008-09-14 02:09:53 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2008-09-14 02:09:53 +0200 |
commit | 6ff238c6397298f2d241a280bd19feb2fa0e7e7b (patch) | |
tree | 2876073efcbebb86a1cab2cf046545a117648214 | |
parent | e395a272e9277dd10cc8b30011aea937774e99c3 (diff) | |
parent | df041f69a2e7ecb8b3047e9296fd57df60b35757 (diff) |
Merge branch 'encrypt' of git://gitorious.org/bluez/cktakahasis-clone
-rw-r--r-- | common/glib-helper.c | 187 | ||||
-rw-r--r-- | common/glib-helper.h | 4 | ||||
-rw-r--r-- | input/device.c | 214 |
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; } |