diff options
author | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2009-01-11 10:18:23 -0300 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@nokia.com> | 2009-01-11 19:11:59 +0200 |
commit | 2aad1cf41acbc69481915d24cef33fa721d4e1fd (patch) | |
tree | 3415f14eb928bc6da4c86bd8600697d31c062c97 /src | |
parent | 60ec17b68922904833973af118ff91e0f1df1b04 (diff) |
Move bonding creation to device.c.
Diffstat (limited to 'src')
-rw-r--r-- | src/adapter.c | 694 | ||||
-rw-r--r-- | src/adapter.h | 36 | ||||
-rw-r--r-- | src/dbus-hci.c | 358 | ||||
-rw-r--r-- | src/device.c | 692 | ||||
-rw-r--r-- | src/device.h | 27 |
5 files changed, 784 insertions, 1023 deletions
diff --git a/src/adapter.c b/src/adapter.c index 21db540e..5df012e6 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -108,9 +108,6 @@ struct btd_adapter { GSList *passkey_agents; struct agent *agent; /* For the new API */ GSList *active_conn; - struct bonding_request_info *bonding; - GSList *auth_reqs; /* Received and replied HCI - authentication requests */ GSList *devices; /* Devices structure pointers */ GSList *mode_sessions; /* Request Mode sessions */ GSList *disc_sessions; /* Discovery sessions */ @@ -179,59 +176,6 @@ static inline DBusMessage *unsupported_major_class(DBusMessage *msg) "Unsupported Major Class"); } -static DBusHandlerResult error_failed(DBusConnection *conn, - DBusMessage *msg, const char * desc) -{ - return error_common_reply(conn, msg, ERROR_INTERFACE ".Failed", desc); -} - -static DBusHandlerResult error_failed_errno(DBusConnection *conn, - DBusMessage *msg, int err) -{ - const char *desc = strerror(err); - - return error_failed(conn, msg, desc); -} - -static DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn, - DBusMessage *msg, int err) -{ - return error_common_reply(conn, msg, - ERROR_INTERFACE ".ConnectionAttemptFailed", - err > 0 ? strerror(err) : "Connection attempt failed"); -} - -static void bonding_request_free(struct bonding_request_info *bonding) -{ - struct btd_device *device; - char address[18]; - struct agent *agent; - - if (!bonding) - return; - - if (bonding->msg) - dbus_message_unref(bonding->msg); - - if (bonding->conn) - dbus_connection_unref(bonding->conn); - - if (bonding->io) - g_io_channel_unref(bonding->io); - - ba2str(&bonding->bdaddr, address); - - device = adapter_find_device(bonding->adapter, address); - agent = device_get_agent(device); - - if (device && agent) { - agent_destroy(agent, FALSE); - device_set_agent(device, NULL); - } - - g_free(bonding); -} - static int active_conn_find_by_bdaddr(const void *data, const void *user_data) { const struct active_conn_info *con = data; @@ -285,62 +229,6 @@ static int found_device_cmp(const struct remote_dev_info *d1, return 0; } -static int auth_req_cmp(const void *p1, const void *p2) -{ - const struct pending_auth_info *pb1 = p1; - const bdaddr_t *bda = p2; - - return bda ? bacmp(&pb1->bdaddr, bda) : -1; -} - -struct pending_auth_info *adapter_find_auth_request(struct btd_adapter *adapter, - bdaddr_t *dba) -{ - GSList *l; - - l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp); - if (l) - return l->data; - - return NULL; -} - -void adapter_remove_auth_request(struct btd_adapter *adapter, bdaddr_t *dba) -{ - GSList *l; - struct pending_auth_info *auth; - - l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp); - if (!l) - return; - - auth = l->data; - - adapter->auth_reqs = g_slist_remove(adapter->auth_reqs, auth); - - g_free(auth); -} - -struct pending_auth_info *adapter_new_auth_request(struct btd_adapter *adapter, - bdaddr_t *dba, - auth_type_t type) -{ - struct pending_auth_info *info; - - debug("hcid_dbus_new_auth_request"); - - info = g_new0(struct pending_auth_info, 1); - - bacpy(&info->bdaddr, dba); - info->type = type; - adapter->auth_reqs = g_slist_append(adapter->auth_reqs, info); - - if (adapter->bonding && !bacmp(dba, &adapter->bonding->bdaddr)) - adapter->bonding->auth_active = 1; - - return info; -} - static void dev_info_free(struct remote_dev_info *dev) { g_free(dev->alias); @@ -384,91 +272,6 @@ int pending_remote_name_cancel(struct btd_adapter *adapter) return err; } -static int auth_info_agent_cmp(const void *a, const void *b) -{ - const struct pending_auth_info *auth = a; - const struct agent *agent = b; - - if (auth->agent == agent) - return 0; - - return -1; -} - -static void device_agent_removed(struct agent *agent, void *user_data) -{ - struct btd_device *device = user_data; - struct pending_auth_info *auth; - GSList *l; - struct btd_adapter *adapter; - - adapter = device_get_adapter(device); - device_set_agent(device, NULL); - - l = g_slist_find_custom(adapter->auth_reqs, agent, - auth_info_agent_cmp); - if (!l) - return; - - auth = l->data; - auth->agent = NULL; -} - -static struct bonding_request_info *bonding_request_new(DBusConnection *conn, - DBusMessage *msg, - struct btd_adapter *adapter, - const char *address, - const char *agent_path, - uint8_t capability) -{ - struct bonding_request_info *bonding; - struct btd_device *device; - const char *name = dbus_message_get_sender(msg); - struct agent *agent; - char addr[18]; - bdaddr_t bdaddr; - - debug("bonding_request_new(%s)", address); - - device = adapter_get_device(conn, adapter, address); - if (!device) - return NULL; - - device_get_address(device, &bdaddr); - ba2str(&bdaddr, addr); - - if (adapter->agent && - agent_matches(adapter->agent, name, agent_path)) { - error("Refusing adapter agent usage as device specific one"); - return NULL; - } - - agent = agent_create(adapter, name, agent_path, - capability, - device_agent_removed, - device); - if (!agent) { - error("Unable to create a new agent"); - return NULL; - } - - device_set_agent(device, agent); - - debug("Temporary agent registered for hci%d/%s at %s:%s", - adapter->dev_id, addr, name, - agent_path); - - bonding = g_new0(struct bonding_request_info, 1); - - bonding->conn = dbus_connection_ref(conn); - bonding->msg = dbus_message_ref(msg); - bonding->adapter = adapter; - - str2ba(address, &bonding->bdaddr); - - return bonding; -} - static int set_limited_discoverable(int dd, const uint8_t *cls, gboolean limited) { uint32_t dev_class; @@ -1079,19 +882,6 @@ static DBusMessage *set_name(DBusConnection *conn, DBusMessage *msg, return dbus_message_new_method_return(msg); } -static void reply_authentication_failure(struct bonding_request_info *bonding) -{ - DBusMessage *reply; - int status; - - status = bonding->hci_status ? - bonding->hci_status : HCI_AUTHENTICATION_FAILURE; - - reply = new_authentication_return(bonding->msg, status); - if (reply) - g_dbus_send_message(bonding->conn, reply); -} - struct btd_device *adapter_find_device(struct btd_adapter *adapter, const char *dest) { struct btd_device *device; @@ -1156,93 +946,6 @@ struct btd_device *adapter_create_device(DBusConnection *conn, return device; } -static DBusMessage *remove_bonding(DBusConnection *conn, DBusMessage *msg, - const char *address, void *data) -{ - struct btd_adapter *adapter = data; - struct btd_device *device; - char filename[PATH_MAX + 1]; - char *str, srcaddr[18]; - bdaddr_t dst; - GSList *l; - int dev; - gboolean paired; - - str2ba(address, &dst); - ba2str(&adapter->bdaddr, srcaddr); - - dev = hci_open_dev(adapter->dev_id); - if (dev < 0 && msg) - return no_such_adapter(msg); - - create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, - "linkkeys"); - - /* textfile_del doesn't return an error when the key is not found */ - str = textfile_caseget(filename, address); - paired = str ? TRUE : FALSE; - g_free(str); - - if (!paired && msg) { - hci_close_dev(dev); - return g_dbus_create_error(msg, - ERROR_INTERFACE ".DoesNotExist", - "Bonding does not exist"); - } - - /* Delete the link key from storage */ - if (textfile_casedel(filename, address) < 0 && msg) { - int err = errno; - hci_close_dev(dev); - return failed_strerror(msg, err); - } - - /* Delete the link key from the Bluetooth chip */ - hci_delete_stored_link_key(dev, &dst, 0, HCI_REQ_TIMEOUT); - - /* find the connection */ - l = g_slist_find_custom(adapter->active_conn, &dst, - active_conn_find_by_bdaddr); - if (l) { - struct active_conn_info *con = l->data; - /* Send the HCI disconnect command */ - if ((hci_disconnect(dev, htobs(con->handle), - HCI_OE_USER_ENDED_CONNECTION, - HCI_REQ_TIMEOUT) < 0) - && msg){ - int err = errno; - error("Disconnect failed"); - hci_close_dev(dev); - return failed_strerror(msg, err); - } - } - - hci_close_dev(dev); - - device = adapter_find_device(adapter, address); - if (!device) - goto proceed; - - if (paired) { - gboolean paired = FALSE; - - const gchar *dev_path = device_get_path(device); - - emit_property_changed(conn, dev_path, DEVICE_INTERFACE, - "Paired", DBUS_TYPE_BOOLEAN, &paired); - } - -proceed: - if(!msg) - goto done; - - return dbus_message_new_method_return(msg); - -done: - return NULL; -} - - void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter, struct btd_device *device) { @@ -1257,9 +960,6 @@ void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter, delete_entry(&adapter->bdaddr, "profiles", dstaddr); adapter->devices = g_slist_remove(adapter->devices, device); - if (!device_is_temporary(device)) - remove_bonding(conn, NULL, dstaddr, adapter); - adapter_update_devices(adapter); g_dbus_emit_signal(conn, adapter->path, @@ -1274,7 +974,7 @@ void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter, device_set_agent(device, NULL); } - device_remove(conn, device); + device_remove(device, conn); } struct btd_device *adapter_get_device(DBusConnection *conn, @@ -1294,292 +994,6 @@ struct btd_device *adapter_get_device(DBusConnection *conn, return adapter_create_device(conn, adapter, address); } -void remove_pending_device(struct btd_adapter *adapter) -{ - struct btd_device *device; - char address[18]; - - ba2str(&adapter->bonding->bdaddr, address); - device = adapter_find_device(adapter, address); - if (!device) - return; - - if (device_is_temporary(device)) - adapter_remove_device(adapter->bonding->conn, adapter, device); -} - -static gboolean create_bonding_io_cb(GIOChannel *io, GIOCondition cond, - struct btd_adapter *adapter) -{ - struct hci_request rq; - auth_requested_cp cp; - evt_cmd_status rp; - struct l2cap_conninfo cinfo; - socklen_t len; - int sk, dd, ret; - - if (!adapter->bonding) { - /* If we come here it implies a bug somewhere */ - debug("create_bonding_io_cb: no pending bonding!"); - g_io_channel_close(io); - return FALSE; - } - - if (cond & G_IO_NVAL) { - DBusMessage *reply; - reply = new_authentication_return(adapter->bonding->msg, 0x09); - g_dbus_send_message(adapter->bonding->conn, reply); - goto cleanup; - } - - if (cond & (G_IO_HUP | G_IO_ERR)) { - debug("Hangup or error on bonding IO channel"); - - if (!adapter->bonding->auth_active) - error_connection_attempt_failed(adapter->bonding->conn, - adapter->bonding->msg, - ENETDOWN); - else - reply_authentication_failure(adapter->bonding); - - goto failed; - } - - sk = g_io_channel_unix_get_fd(io); - - len = sizeof(ret); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - error("Can't get socket error: %s (%d)", - strerror(errno), errno); - error_failed_errno(adapter->bonding->conn, adapter->bonding->msg, - errno); - goto failed; - } - - if (ret != 0) { - if (adapter->bonding->auth_active) - reply_authentication_failure(adapter->bonding); - else - error_connection_attempt_failed(adapter->bonding->conn, - adapter->bonding->msg, - ret); - goto failed; - } - - len = sizeof(cinfo); - if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &cinfo, &len) < 0) { - error("Can't get connection info: %s (%d)", - strerror(errno), errno); - error_failed_errno(adapter->bonding->conn, adapter->bonding->msg, - errno); - goto failed; - } - - dd = hci_open_dev(adapter->dev_id); - if (dd < 0) { - DBusMessage *reply = no_such_adapter(adapter->bonding->msg); - g_dbus_send_message(adapter->bonding->conn, reply); - goto failed; - } - - memset(&rp, 0, sizeof(rp)); - - memset(&cp, 0, sizeof(cp)); - cp.handle = htobs(cinfo.hci_handle); - - memset(&rq, 0, sizeof(rq)); - rq.ogf = OGF_LINK_CTL; - rq.ocf = OCF_AUTH_REQUESTED; - rq.cparam = &cp; - rq.clen = AUTH_REQUESTED_CP_SIZE; - rq.rparam = &rp; - rq.rlen = EVT_CMD_STATUS_SIZE; - rq.event = EVT_CMD_STATUS; - - if (hci_send_req(dd, &rq, HCI_REQ_TIMEOUT) < 0) { - error("Unable to send HCI request: %s (%d)", - strerror(errno), errno); - error_failed_errno(adapter->bonding->conn, adapter->bonding->msg, - errno); - hci_close_dev(dd); - goto failed; - } - - if (rp.status) { - error("HCI_Authentication_Requested failed with status 0x%02x", - rp.status); - error_failed_errno(adapter->bonding->conn, adapter->bonding->msg, - bt_error(rp.status)); - hci_close_dev(dd); - goto failed; - } - - hci_close_dev(dd); - - adapter->bonding->auth_active = 1; - - adapter->bonding->io_id = g_io_add_watch(io, - G_IO_NVAL | G_IO_HUP | G_IO_ERR, - (GIOFunc) create_bonding_io_cb, - adapter); - - return FALSE; - -failed: - g_io_channel_close(io); - remove_pending_device(adapter); - -cleanup: - g_dbus_remove_watch(adapter->bonding->conn, - adapter->bonding->listener_id); - bonding_request_free(adapter->bonding); - adapter->bonding = NULL; - - return FALSE; -} - -static void cancel_auth_request(struct pending_auth_info *auth, int dev_id) -{ - int dd; - - if (auth->replied) - return; - - dd = hci_open_dev(dev_id); - if (dd < 0) { - error("hci_open_dev: %s (%d)", strerror(errno), errno); - return; - } - - switch (auth->type) { - case AUTH_TYPE_PINCODE: - hci_send_cmd(dd, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, - 6, &auth->bdaddr); - break; - case AUTH_TYPE_CONFIRM: - hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY, - 6, &auth->bdaddr); - break; - case AUTH_TYPE_PASSKEY: - hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_PASSKEY_NEG_REPLY, - 6, &auth->bdaddr); - break; - case AUTH_TYPE_NOTIFY: - /* User Notify doesn't require any reply */ - break; - } - - auth->replied = TRUE; - - hci_close_dev(dd); -} - -static void cancel_bonding(struct btd_adapter *adapter, gboolean exited) -{ - struct pending_auth_info *auth; - struct bonding_request_info *bonding = adapter->bonding; - - auth = adapter_find_auth_request(adapter, &adapter->bdaddr); - if (auth) { - cancel_auth_request(auth, adapter->dev_id); - if (auth->agent) - agent_cancel(auth->agent); - adapter_remove_auth_request(adapter, &bonding->bdaddr); - } - - remove_pending_device(adapter); - - if (bonding->io) - g_io_channel_close(bonding->io); - - if (exited) { - if (bonding->io_id) { - g_source_remove(bonding->io_id); - bonding->io_id = 0; - } - bonding_request_free(bonding); - adapter->bonding = NULL; - - } else - bonding->cancel = TRUE; -} - -static void create_bond_req_exit(DBusConnection *conn, void *user_data) -{ - struct btd_adapter *adapter = user_data; - - debug("CreateConnection requestor exited before bonding was completed"); - - cancel_bonding(adapter, TRUE); -} - -static DBusMessage *create_bonding(DBusConnection *conn, DBusMessage *msg, - const char *address, const char *agent_path, - uint8_t capability, void *data) -{ - char filename[PATH_MAX + 1]; - char *str, srcaddr[18]; - struct btd_adapter *adapter = data; - struct bonding_request_info *bonding; - bdaddr_t dst; - int sk; - - str2ba(address, &dst); - ba2str(&adapter->bdaddr, srcaddr); - - /* check if there is a pending discover: requested by D-Bus/non clients */ - if (adapter->state & STD_INQUIRY) - return in_progress(msg, "Discover in progress"); - - pending_remote_name_cancel(adapter); - - if (adapter->bonding) - return in_progress(msg, "Bonding in progress"); - - if (adapter_find_auth_request(adapter, &dst)) - return in_progress(msg, "Bonding in progress"); - - /* check if a link key already exists */ - create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, - "linkkeys"); - - str = textfile_caseget(filename, address); - if (str) { - free(str); - return g_dbus_create_error(msg, - ERROR_INTERFACE ".AlreadyExists", - "Bonding already exists"); - } - - sk = l2raw_connect(&adapter->bdaddr, &dst); - if (sk < 0) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".ConnectionAttemptFailed", - "Connection attempt failed"); - - bonding = bonding_request_new(conn, msg, adapter, address, agent_path, - capability); - if (!bonding) { - close(sk); - return NULL; - } - - bonding->io = g_io_channel_unix_new(sk); - bonding->io_id = g_io_add_watch(bonding->io, - G_IO_OUT | G_IO_NVAL | G_IO_HUP | G_IO_ERR, - (GIOFunc) create_bonding_io_cb, - adapter); - - bonding->listener_id = g_dbus_add_disconnect_watch(conn, - dbus_message_get_sender(msg), - create_bond_req_exit, adapter, - NULL); - - adapter->bonding = bonding; - - return NULL; -} - static int start_inquiry(struct btd_adapter *adapter) { inquiry_cp cp; @@ -2010,9 +1424,8 @@ static DBusMessage *cancel_device_creation(DBusConnection *conn, DBusMessage *msg, void *data) { struct btd_adapter *adapter = data; - struct bonding_request_info *bonding = adapter->bonding; const gchar *address; - bdaddr_t bda; + struct btd_device *device; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID) == FALSE) @@ -2021,16 +1434,17 @@ static DBusMessage *cancel_device_creation(DBusConnection *conn, if (check_address(address) < 0) return invalid_args(msg); - str2ba(address, &bda); + device = adapter_find_device(adapter, address); + if (!device) + return g_dbus_create_error(msg, + ERROR_INTERFACE ".DoesNotExist", + "Device does not exist"); - if (bonding && !bacmp(&bonding->bdaddr, &bda)) { - if (!g_str_equal(dbus_message_get_sender(msg), - dbus_message_get_sender(bonding->msg))) - return not_authorized(msg); + if (!device_is_temporary(device) || + !device_is_bonding(device, dbus_message_get_sender(msg))) + return not_authorized(msg); - debug("Canceling device creation for %s", address); - cancel_bonding(adapter, FALSE); - } + adapter_remove_device(conn, adapter, device); return dbus_message_new_method_return(msg); } @@ -2085,7 +1499,9 @@ static uint8_t parse_io_capability(const char *capability) static DBusMessage *create_paired_device(DBusConnection *conn, DBusMessage *msg, void *data) { - const gchar *address, *agent_path, *capability; + struct btd_adapter *adapter = data; + struct btd_device *device; + const gchar *address, *agent_path, *capability, *sender; uint8_t cap; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address, @@ -2097,11 +1513,20 @@ static DBusMessage *create_paired_device(DBusConnection *conn, if (check_address(address) < 0) return invalid_args(msg); + sender = dbus_message_get_sender(msg); + if (adapter->agent && + agent_matches(adapter->agent, sender, agent_path)) { + error("Refusing adapter agent usage as device specific one"); + return invalid_args(msg); + } + cap = parse_io_capability(capability); if (cap == IO_CAPABILITY_INVALID) return invalid_args(msg); - return create_bonding(conn, msg, address, agent_path, cap, data); + device = adapter_get_device(conn, adapter, address); + + return device_create_bonding(device, conn, msg, agent_path, cap); } static gint device_path_cmp(struct btd_device *device, const gchar *path) @@ -2179,18 +1604,7 @@ static DBusMessage *find_device(DBusConnection *conn, static void agent_removed(struct agent *agent, struct btd_adapter *adapter) { - struct pending_auth_info *auth; - GSList *l; - adapter->agent = NULL; - - l = g_slist_find_custom(adapter->auth_reqs, agent, - auth_info_agent_cmp); - if (!l) - return; - - auth = l->data; - auth->agent = NULL; } static DBusMessage *register_agent(DBusConnection *conn, @@ -2725,27 +2139,18 @@ setup: static void reply_pending_requests(struct btd_adapter *adapter) { - DBusMessage *reply; + GSList *l; if (!adapter) return; /* pending bonding */ - if (adapter->bonding) { - reply = new_authentication_return(adapter->bonding->msg, - HCI_OE_USER_ENDED_CONNECTION); - g_dbus_send_message(connection, reply); - remove_pending_device(adapter); - - g_dbus_remove_watch(adapter->bonding->conn, - adapter->bonding->listener_id); + for (l = adapter->devices; l; l = l->next) { + struct btd_device *device = l->data; - if (adapter->bonding->io_id) - g_source_remove(adapter->bonding->io_id); - if (adapter->bonding->io) - g_io_channel_close(adapter->bonding->io); - bonding_request_free(adapter->bonding); - adapter->bonding = NULL; + if (device_is_bonding(device, NULL)) + device_cancel_bonding(device, + HCI_OE_USER_ENDED_CONNECTION); } if (adapter->state & STD_INQUIRY) { @@ -2806,12 +2211,6 @@ int adapter_stop(struct btd_adapter *adapter) adapter->oor_devices = NULL; } - if (adapter->auth_reqs) { - g_slist_foreach(adapter->auth_reqs, (GFunc) g_free, NULL); - g_slist_free(adapter->auth_reqs); - adapter->auth_reqs = NULL; - } - if (adapter->active_conn) { g_slist_foreach(adapter->active_conn, (GFunc) g_free, NULL); g_slist_free(adapter->active_conn); @@ -2958,7 +2357,7 @@ void adapter_remove(struct btd_adapter *adapter) unload_drivers(adapter); for (l = adapter->devices; l; l = l->next) - device_remove(connection, l->data); + device_remove(l->data, connection); g_slist_free(adapter->devices); /* Return adapter to down state if it was not up on init */ @@ -3304,29 +2703,6 @@ struct active_conn_info *adapter_search_active_conn_by_handle(struct btd_adapter return NULL; } -void adapter_free_bonding_request(struct btd_adapter *adapter) -{ - g_dbus_remove_watch(connection, adapter->bonding->listener_id); - - if (adapter->bonding->io_id) - g_source_remove(adapter->bonding->io_id); - - if (adapter->bonding->io) - g_io_channel_close(adapter->bonding->io); - - bonding_request_free(adapter->bonding); - - adapter->bonding = NULL; -} - -struct bonding_request_info *adapter_get_bonding_info(struct btd_adapter *adapter) -{ - if (!adapter) - return NULL; - - return adapter->bonding; -} - gboolean adapter_has_discov_sessions(struct btd_adapter *adapter) { if (!adapter || !adapter->disc_sessions) @@ -3521,11 +2897,3 @@ gboolean adapter_is_pairable(struct btd_adapter *adapter) { return adapter->pairable; } - -gboolean adapter_pairing_initiator(struct btd_adapter *adapter, bdaddr_t *bda) -{ - if (!adapter->bonding) - return FALSE; - - return (bacmp(&adapter->bonding->bdaddr, bda) == 0); -} diff --git a/src/adapter.h b/src/adapter.h index 0ca6842c..2f9068f2 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -43,12 +43,7 @@ typedef enum { NAME_SENT /* D-Bus signal RemoteNameUpdated sent */ } name_status_t; -typedef enum { - AUTH_TYPE_PINCODE, - AUTH_TYPE_PASSKEY, - AUTH_TYPE_CONFIRM, - AUTH_TYPE_NOTIFY, -} auth_type_t; +struct btd_adapter; struct remote_dev_info { bdaddr_t bdaddr; @@ -58,26 +53,6 @@ struct remote_dev_info { name_status_t name_status; }; -struct bonding_request_info { - DBusConnection *conn; - DBusMessage *msg; - struct btd_adapter *adapter; - bdaddr_t bdaddr; - GIOChannel *io; - guint io_id; - guint listener_id; - int hci_status; - int cancel; - int auth_active; -}; - -struct pending_auth_info { - auth_type_t type; - bdaddr_t bdaddr; - gboolean replied; /* If we've already replied to the request */ - struct agent *agent; /* Agent associated with the request */ -}; - struct active_conn_info { bdaddr_t bdaddr; uint16_t handle; @@ -123,12 +98,6 @@ int pending_remote_name_cancel(struct btd_adapter *adapter); void remove_pending_device(struct btd_adapter *adapter); -struct pending_auth_info *adapter_find_auth_request(struct btd_adapter *adapter, - bdaddr_t *dba); -void adapter_remove_auth_request(struct btd_adapter *adapter, bdaddr_t *dba); -struct pending_auth_info *adapter_new_auth_request(struct btd_adapter *adapter, - bdaddr_t *dba, - auth_type_t type); struct btd_adapter *adapter_create(DBusConnection *conn, int id, gboolean devup); void adapter_remove(struct btd_adapter *adapter); @@ -155,8 +124,6 @@ struct active_conn_info *adapter_search_active_conn_by_bdaddr(struct btd_adapter bdaddr_t *bda); struct active_conn_info *adapter_search_active_conn_by_handle(struct btd_adapter *adapter, uint16_t handle); -void adapter_free_bonding_request(struct btd_adapter *adapter); -struct bonding_request_info *adapter_get_bonding_info(struct btd_adapter *adapter); gboolean adapter_has_discov_sessions(struct btd_adapter *adapter); struct btd_adapter_driver { @@ -178,4 +145,3 @@ const char *adapter_any_get_path(void); const char *btd_adapter_any_request_path(void); void btd_adapter_any_release_path(void); gboolean adapter_is_pairable(struct btd_adapter *adapter); -gboolean adapter_pairing_initiator(struct btd_adapter *adapter, bdaddr_t *bda); diff --git a/src/dbus-hci.c b/src/dbus-hci.c index 7432f018..0e574aff 100644 --- a/src/dbus-hci.c +++ b/src/dbus-hci.c @@ -119,54 +119,6 @@ const char *class_to_icon(uint32_t class) return NULL; } -DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status) -{ - switch (status) { - case 0x00: /* success */ - return dbus_message_new_method_return(msg); - - case 0x04: /* page timeout */ - case 0x08: /* connection timeout */ - case 0x10: /* connection accept timeout */ - case 0x22: /* LMP response timeout */ - case 0x28: /* instant passed - is this a timeout? */ - return dbus_message_new_error(msg, - ERROR_INTERFACE ".AuthenticationTimeout", - "Authentication Timeout"); - case 0x17: /* too frequent pairing attempts */ - return dbus_message_new_error(msg, - ERROR_INTERFACE ".RepeatedAttempts", - "Repeated Attempts"); - - case 0x06: - case 0x18: /* pairing not allowed (e.g. gw rejected attempt) */ - return dbus_message_new_error(msg, - ERROR_INTERFACE ".AuthenticationRejected", - "Authentication Rejected"); - - case 0x07: /* memory capacity */ - case 0x09: /* connection limit */ - case 0x0a: /* synchronous connection limit */ - case 0x0d: /* limited resources */ - case 0x13: /* user ended the connection */ - case 0x14: /* terminated due to low resources */ - return dbus_message_new_error(msg, - ERROR_INTERFACE ".AuthenticationCanceled", - "Authentication Canceled"); - - case 0x05: /* authentication failure */ - case 0x0E: /* rejected due to security reasons - is this auth failure? */ - case 0x25: /* encryption mode not acceptable - is this auth failure? */ - case 0x26: /* link key cannot be changed - is this auth failure? */ - case 0x29: /* pairing with unit key unsupported - is this auth failure? */ - case 0x2f: /* insufficient security - is this auth failure? */ - default: - return dbus_message_new_error(msg, - ERROR_INTERFACE ".AuthenticationFailed", - "Authentication Failed"); - } -} - /***************************************************************** * * Section reserved to HCI commands confirmation handling and low @@ -182,13 +134,7 @@ static void pincode_cb(struct agent *agent, DBusError *err, const char *pincode, bdaddr_t sba, dba; size_t len; int dev; - struct pending_auth_info *auth; uint16_t dev_id = adapter_get_dev_id(adapter); - struct bonding_request_info *bonding = adapter_get_bonding_info(adapter); - - /* No need to reply anything if the authentication already failed */ - if (bonding && bonding->hci_status) - return; dev = hci_open_dev(dev_id); if (dev < 0) { @@ -200,8 +146,6 @@ static void pincode_cb(struct agent *agent, DBusError *err, const char *pincode, adapter_get_address(adapter, &sba); device_get_address(device, &dba); - auth = adapter_find_auth_request(adapter, &dba); - if (err) { hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, &dba); @@ -219,10 +163,6 @@ static void pincode_cb(struct agent *agent, DBusError *err, const char *pincode, hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_REPLY, PIN_CODE_REPLY_CP_SIZE, &pr); done: - if (auth) { - auth->replied = TRUE; - auth->agent = NULL; - } hci_close_dev(dev); } @@ -231,8 +171,6 @@ int hcid_dbus_request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci) char addr[18]; struct btd_adapter *adapter; struct btd_device *device; - struct agent *agent = NULL; - int ret; adapter = manager_find_adapter(sba); if (!adapter) { @@ -240,42 +178,21 @@ int hcid_dbus_request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci) return -1; } - if (!adapter_pairing_initiator(adapter, &ci->bdaddr) && - !adapter_is_pairable(adapter)) - return -EPERM; - ba2str(&ci->bdaddr, addr); device = adapter_find_device(adapter, addr); - - if (device) - agent = device_get_agent(device); - - if (!agent) - agent = adapter_get_agent(adapter); - - if (!agent) { - error("No agent available for PIN request"); + /* Check if the adapter is not pairable and if there isn't a bonding in + * progress */ + if (!adapter_is_pairable(adapter) && + !(device && device_is_bonding(device, NULL))) return -EPERM; - } - if (!device) { - device = adapter_create_device(connection, adapter, addr); - if (!device) - return -ENODEV; - } - - ret = agent_request_pincode(agent, device, - (agent_pincode_cb) pincode_cb, - device); - if (ret == 0) { - struct pending_auth_info *auth; - auth = adapter_new_auth_request(adapter, &ci->bdaddr, - AUTH_TYPE_PINCODE); - auth->agent = agent; - } + device = adapter_get_device(connection, adapter, addr); + if (!device) + return -ENODEV; - return ret; + return device_request_authentication(device, AUTH_TYPE_PINCODE, + 0, pincode_cb); } static void confirm_cb(struct agent *agent, DBusError *err, void *user_data) @@ -284,13 +201,7 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *user_data) struct btd_adapter *adapter = device_get_adapter(device); user_confirm_reply_cp cp; int dd; - struct pending_auth_info *auth; uint16_t dev_id = adapter_get_dev_id(adapter); - struct bonding_request_info *bonding = adapter_get_bonding_info(adapter); - - /* No need to reply anything if the authentication already failed */ - if (bonding && bonding->hci_status) - return; dd = hci_open_dev(dev_id); if (dd < 0) { @@ -301,8 +212,6 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *user_data) memset(&cp, 0, sizeof(cp)); device_get_address(device, &cp.bdaddr); - auth = adapter_find_auth_request(adapter, &cp.bdaddr); - if (err) hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY, USER_CONFIRM_REPLY_CP_SIZE, &cp); @@ -310,11 +219,6 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *user_data) hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_CONFIRM_REPLY, USER_CONFIRM_REPLY_CP_SIZE, &cp); - if (auth) { - auth->replied = TRUE; - auth->agent = FALSE; - } - hci_close_dev(dd); } @@ -326,13 +230,7 @@ static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey, user_passkey_reply_cp cp; bdaddr_t dba; int dd; - struct pending_auth_info *auth; uint16_t dev_id = adapter_get_dev_id(adapter); - struct bonding_request_info *bonding = adapter_get_bonding_info(adapter); - - /* No need to reply anything if the authentication already failed */ - if (bonding && bonding->hci_status) - return; dd = hci_open_dev(dev_id); if (dd < 0) { @@ -346,8 +244,6 @@ static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey, bacpy(&cp.bdaddr, &dba); cp.passkey = passkey; - auth = adapter_find_auth_request(adapter, &dba); - if (err) hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_PASSKEY_NEG_REPLY, 6, &dba); @@ -355,11 +251,6 @@ static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey, hci_send_cmd(dd, OGF_LINK_CTL, OCF_USER_PASSKEY_REPLY, USER_PASSKEY_REPLY_CP_SIZE, &cp); - if (auth) { - auth->replied = TRUE; - auth->agent = NULL; - } - hci_close_dev(dd); } @@ -403,10 +294,8 @@ int hcid_dbus_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey) { struct btd_adapter *adapter; struct btd_device *device; - struct agent *agent; char addr[18]; uint8_t type; - struct pending_auth_info *auth; uint16_t dev_id; adapter = manager_find_adapter(sba); @@ -457,41 +346,19 @@ int hcid_dbus_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey) hci_close_dev(dd); - auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_CONFIRM); - auth->replied = TRUE; - - return 0; - } - - agent = device_get_agent(device); - - if (!agent) - agent = adapter_get_agent(adapter); - - if (!agent) { - error("No agent available for user confirm request"); - return -1; - } - - if (agent_request_confirmation(agent, device, passkey, - confirm_cb, device) < 0) { - error("Requesting passkey failed"); - return -1; + return device_request_authentication(device, AUTH_TYPE_AUTO, + 0, NULL); } - auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_CONFIRM); - auth->agent = agent; - - return 0; + return device_request_authentication(device, AUTH_TYPE_CONFIRM, + passkey, confirm_cb); } int hcid_dbus_user_passkey(bdaddr_t *sba, bdaddr_t *dba) { struct btd_adapter *adapter; struct btd_device *device; - struct agent *agent = NULL; char addr[18]; - struct pending_auth_info *auth; adapter = manager_find_adapter(sba); if (!adapter) { @@ -503,35 +370,18 @@ int hcid_dbus_user_passkey(bdaddr_t *sba, bdaddr_t *dba) device = adapter_get_device(connection, adapter, addr); - if (device) - agent = device_get_agent(device); - - if (!agent) - agent = adapter_get_agent(adapter); - - if (!agent) { - error("No agent available for user passkey request"); - return -1; - } - - if (agent_request_passkey(agent, device, passkey_cb, device) < 0) { - error("Requesting passkey failed"); - return -1; - } - - auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_PASSKEY); - auth->agent = agent; + if (!device) + return -ENODEV; - return 0; + return device_request_authentication(device, AUTH_TYPE_PASSKEY, + 0, passkey_cb); } int hcid_dbus_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey) { struct btd_adapter *adapter; struct btd_device *device; - struct agent *agent = NULL; char addr[18]; - struct pending_auth_info *auth; adapter = manager_find_adapter(sba); if (!adapter) { @@ -542,26 +392,11 @@ int hcid_dbus_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey) ba2str(dba, addr); device = adapter_get_device(connection, adapter, addr); - if (device) - agent = device_get_agent(device); - - if (!agent) - agent = adapter_get_agent(adapter); - - if (!agent) { - error("No agent available for user confirm request"); - return -1; - } - - if (agent_display_passkey(agent, device, passkey) < 0) { - error("Displaying passkey failed"); - return -1; - } - - auth = adapter_new_auth_request(adapter, dba, AUTH_TYPE_NOTIFY); - auth->agent = agent; + if (!device) + return -ENODEV; - return 0; + return device_request_authentication(device, AUTH_TYPE_NOTIFY, + passkey, NULL); } void hcid_dbus_bonding_process_complete(bdaddr_t *local, bdaddr_t *peer, @@ -569,74 +404,35 @@ void hcid_dbus_bonding_process_complete(bdaddr_t *local, bdaddr_t *peer, { struct btd_adapter *adapter; char peer_addr[18]; - DBusMessage *reply; struct btd_device *device; - struct bonding_request_info *bonding; - struct pending_auth_info *auth; debug("hcid_dbus_bonding_process_complete: status=%02x", status); - ba2str(peer, peer_addr); - adapter = manager_find_adapter(local); if (!adapter) { error("Unable to find matching adapter"); return; } - bonding = adapter_get_bonding_info(adapter); - - if (bonding && bacmp(&bonding->bdaddr, peer)) - bonding = NULL; + ba2str(peer, peer_addr); - if (status == 0) { - device = adapter_get_device(connection, adapter, peer_addr); - if (!device) { - /* This should really only happen if we run out of - * memory */ - error("Unable to get device object!"); - status = HCI_REJECTED_LIMITED_RESOURCES; - } + device = adapter_find_device(adapter, peer_addr); + if (!device) { + error("Unable to get device object!"); + return; } - if (status && bonding) - bonding->hci_status = status; - - auth = adapter_find_auth_request(adapter, peer); - if (!auth) { - /* This means that there was no pending PIN or SSP token request - * from the controller, i.e. this is not a new pairing */ + if (!device_is_authenticating(device)) { + /* This means that there was no pending PIN or SSP token + * request from the controller, i.e. this is not a new + * pairing */ debug("hcid_dbus_bonding_process_complete: no pending auth request"); - goto proceed; - } - - if (auth->agent) - agent_cancel(auth->agent); - - adapter_remove_auth_request(adapter, peer); - - /* If this is a new pairing send the appropriate signal for it - * and proceed with service discovery */ - if (status == 0) { - device_set_paired(connection, device, bonding); - if (bonding) - adapter_free_bonding_request(adapter); return; } -proceed: - if (!bonding) - return; /* skip: no bonding req pending */ - - if (bonding->cancel) - reply = new_authentication_return(bonding->msg, - HCI_OE_USER_ENDED_CONNECTION); - else - reply = new_authentication_return(bonding->msg, status); - - g_dbus_send_message(connection, reply); - - adapter_free_bonding_request(adapter); + /* If this is a new pairing send the appropriate reply and signal for + * it and proceed with service discovery */ + device_bonding_complete(device, status); } void hcid_dbus_inquiry_start(bdaddr_t *local) @@ -1125,7 +921,6 @@ void hcid_dbus_link_key_notify(bdaddr_t *local, bdaddr_t *peer) char peer_addr[18]; struct btd_device *device; struct btd_adapter *adapter; - struct bonding_request_info *bonding; adapter = manager_find_adapter(local); if (!adapter) { @@ -1141,11 +936,9 @@ void hcid_dbus_link_key_notify(bdaddr_t *local, bdaddr_t *peer) return; } - bonding = adapter_get_bonding_info(adapter); - if (!device_get_connected(device)) device_set_secmode3_conn(device, TRUE); - else if (!bonding) + else if (!device_is_bonding(device, NULL)) hcid_dbus_bonding_process_complete(local, peer, 0); } @@ -1154,7 +947,6 @@ void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, { char peer_addr[18]; struct btd_adapter *adapter; - struct bonding_request_info *bonding; struct btd_device *device; adapter = manager_find_adapter(local); @@ -1166,42 +958,32 @@ void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, ba2str(peer, peer_addr); device = adapter_get_device(connection, adapter, peer_addr); + if (!device) { + error("No matching device found"); + return; + } if (status) { - struct pending_auth_info *auth; - - auth = adapter_find_auth_request(adapter, peer); - if (auth && auth->agent) - agent_cancel(auth->agent); - - adapter_remove_auth_request(adapter, peer); - - if (device) - device_set_secmode3_conn(device, FALSE); + device_set_secmode3_conn(device, FALSE); + if (device_is_bonding(device, NULL)) + device_bonding_complete(device, status); + return; + } - bonding = adapter_get_bonding_info(adapter); - if (bonding) - bonding->hci_status = status; - } else { - if (device) - device_set_connected(connection, device, TRUE); + device_set_connected(device, connection, TRUE); - /* add in the active connetions list */ - adapter_add_active_conn(adapter, peer, handle); - } + /* add in the active connetions list */ + adapter_add_active_conn(adapter, peer, handle); } void hcid_dbus_disconn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, uint8_t reason) { - DBusMessage *reply; char peer_addr[18]; struct btd_adapter *adapter; struct btd_device *device; struct active_conn_info *dev; - struct pending_auth_info *auth; uint16_t dev_id; - struct bonding_request_info *bonding; if (status) { error("Disconnection failed: 0x%02x", status); @@ -1227,39 +1009,17 @@ void hcid_dbus_disconn_complete(bdaddr_t *local, uint8_t status, /* clean pending HCI cmds */ hci_req_queue_remove(dev_id, &dev->bdaddr); - /* Cancel D-Bus/non D-Bus requests */ - auth = adapter_find_auth_request(adapter, &dev->bdaddr); - if (auth && auth->agent) - agent_cancel(auth->agent); - - adapter_remove_auth_request(adapter, &dev->bdaddr); - - bonding = adapter_get_bonding_info(adapter); - /* Check if there is a pending Bonding request */ - if (bonding && (bacmp(&bonding->bdaddr, &dev->bdaddr) == 0)) { - if (bonding->cancel) { - /* reply authentication canceled */ - reply = new_authentication_return(bonding->msg, - HCI_OE_USER_ENDED_CONNECTION); - g_dbus_send_message(connection, reply); - } else { - reply = new_authentication_return(bonding->msg, - HCI_AUTHENTICATION_FAILURE); - g_dbus_send_message(connection, reply); - } - adapter_free_bonding_request(adapter); - } - adapter_remove_active_conn(adapter, dev); device = adapter_find_device(adapter, peer_addr); - if (device) { - device_set_connected(connection, device, FALSE); + if (!device) + return; - if (device_is_temporary(device)) { - debug("Removing temporary device %s", peer_addr); - adapter_remove_device(connection, adapter, device); - } + device_set_connected(device, connection, FALSE); + + if (device_is_temporary(device)) { + debug("Removing temporary device %s", peer_addr); + adapter_remove_device(connection, adapter, device); } } @@ -1482,14 +1242,16 @@ int hcid_dbus_get_io_cap(bdaddr_t *local, bdaddr_t *remote, if (get_auth_requirements(local, remote, auth) < 0) return -1; - if (!adapter_pairing_initiator(adapter, remote) && - !adapter_is_pairable(adapter)) - return -EPERM; - ba2str(remote, addr); - /* For CreatePairedDevice use dedicated bonding */ device = adapter_find_device(adapter, addr); + /* Check if the adapter is not pairable and if there isn't a bonding in + * progress */ + if (!adapter_is_pairable(adapter) && + !(device && device_is_bonding(device, NULL))) + return -EPERM; + + /* For CreatePairedDevice use dedicated bonding */ if (device) { agent = device_get_agent(device); if (agent) diff --git a/src/device.c b/src/device.c index f51184ca..7891d1e5 100644 --- a/src/device.c +++ b/src/device.c @@ -35,6 +35,7 @@ #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #include <bluetooth/hci_lib.h> +#include <bluetooth/l2cap.h> #include <bluetooth/sdp.h> #include <bluetooth/sdp_lib.h> @@ -65,6 +66,22 @@ struct btd_driver_data { void *priv; }; +struct bonding_req { + DBusConnection *conn; + DBusMessage *msg; + GIOChannel *io; + guint io_id; + guint listener_id; + struct btd_device *device; +}; + +struct authentication_req { + auth_type_t type; + void *cb; + struct agent *agent; + struct btd_device *device; +}; + struct btd_device { bdaddr_t bdaddr; gchar *path; @@ -78,6 +95,8 @@ struct btd_device { char *discov_requestor; /* discovery requestor unique name */ guint discov_listener; guint discov_timer; + struct bonding_req *bonding; + struct authentication_req *authr; /* authentication request */ /* For Secure Simple Pairing */ uint8_t cap; @@ -111,6 +130,39 @@ static uint16_t uuid_list[] = { static GSList *device_drivers = NULL; +static DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn, + DBusMessage *msg, int err) +{ + return error_common_reply(conn, msg, + ERROR_INTERFACE ".ConnectionAttemptFailed", + err > 0 ? strerror(err) : "Connection attempt failed"); +} + +static DBusHandlerResult error_failed(DBusConnection *conn, + DBusMessage *msg, const char * desc) +{ + return error_common_reply(conn, msg, ERROR_INTERFACE ".Failed", desc); +} + +static DBusHandlerResult error_failed_errno(DBusConnection *conn, + DBusMessage *msg, int err) +{ + const char *desc = strerror(err); + + return error_failed(conn, msg, desc); +} + +static inline DBusMessage *no_such_adapter(DBusMessage *msg) +{ + return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter", + "No such adapter"); +} + +static inline DBusMessage *in_progress(DBusMessage *msg, const char *str) +{ + return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", str); +} + static void device_free(gpointer user_data) { struct btd_device *device = user_data; @@ -254,13 +306,6 @@ static DBusMessage *get_properties(DBusConnection *conn, ptr = adapter_get_path(adapter); dict_append_entry(&dict, "Adapter", DBUS_TYPE_OBJECT_PATH, &ptr); - if (read_remote_eir(&src, &device->bdaddr, NULL) < 0) - boolean = TRUE; - else - boolean = FALSE; - - dict_append_entry(&dict, "LegacyPairing", DBUS_TYPE_BOOLEAN, &boolean); - dbus_message_iter_close_container(&iter, &dict); return reply; @@ -523,7 +568,7 @@ gboolean device_get_connected(struct btd_device *device) return device->connected; } -void device_set_connected(DBusConnection *conn, struct btd_device *device, +void device_set_connected(struct btd_device *device, DBusConnection *conn, gboolean connected) { device->connected = connected; @@ -579,7 +624,61 @@ struct btd_device *device_create(DBusConnection *conn, struct btd_adapter *adapt return device; } -void device_remove(DBusConnection *conn, struct btd_device *device) +static void device_remove_bonding(struct btd_device *device, DBusConnection *conn) +{ + struct active_conn_info *ci; + char filename[PATH_MAX + 1]; + char *str, srcaddr[18], dstaddr[18]; + int dd, dev_id; + bdaddr_t bdaddr; + gboolean paired; + + adapter_get_address(device->adapter, &bdaddr); + ba2str(&bdaddr, srcaddr); + ba2str(&device->bdaddr, dstaddr); + + dev_id = adapter_get_dev_id(device->adapter); + + dd = hci_open_dev(dev_id); + if (dd < 0) + return; + + create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, + "linkkeys"); + + /* textfile_del doesn't return an error when the key is not found */ + str = textfile_caseget(filename, dstaddr); + paired = str ? TRUE : FALSE; + g_free(str); + + if (!paired) + return; + + /* Delete the link key from storage */ + textfile_casedel(filename, dstaddr); + + /* Delete the link key from the Bluetooth chip */ + hci_delete_stored_link_key(dd, &device->bdaddr, 0, HCI_REQ_TIMEOUT); + + /* Send the HCI disconnect command */ + ci = adapter_search_active_conn_by_bdaddr(device->adapter, + &device->bdaddr); + if (ci) { + int err = hci_disconnect(dd, htobs(ci->handle), + HCI_OE_USER_ENDED_CONNECTION, + HCI_REQ_TIMEOUT); + if (err < 0) + error("Disconnect: %s (%d)", strerror(-err), -err); + } + + hci_close_dev(dd); + + paired = FALSE; + emit_property_changed(conn, device->path, DEVICE_INTERFACE, + "Paired", DBUS_TYPE_BOOLEAN, &paired); +} + +void device_remove(struct btd_device *device, DBusConnection *conn) { GSList *list; struct btd_device_driver *driver; @@ -587,6 +686,12 @@ void device_remove(DBusConnection *conn, struct btd_device *device) debug("Removing device %s", path); + if (device->bonding) + device_cancel_bonding(device, HCI_OE_USER_ENDED_CONNECTION); + + if (!device->temporary) + device_remove_bonding(device, conn); + for (list = device->drivers; list; list = list->next) { struct btd_driver_data *driver_data = list->data; driver = driver_data->driver; @@ -1280,15 +1385,356 @@ static gboolean start_discovery(gpointer user_data) return FALSE; } -int device_set_paired(DBusConnection *conn, struct btd_device *device, - struct bonding_request_info *bonding) +DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status) { - dbus_bool_t paired = TRUE; + switch (status) { + case 0x00: /* success */ + return dbus_message_new_method_return(msg); + + case 0x04: /* page timeout */ + case 0x08: /* connection timeout */ + case 0x10: /* connection accept timeout */ + case 0x22: /* LMP response timeout */ + case 0x28: /* instant passed - is this a timeout? */ + return dbus_message_new_error(msg, + ERROR_INTERFACE ".AuthenticationTimeout", + "Authentication Timeout"); + case 0x17: /* too frequent pairing attempts */ + return dbus_message_new_error(msg, + ERROR_INTERFACE ".RepeatedAttempts", + "Repeated Attempts"); + + case 0x06: + case 0x18: /* pairing not allowed (e.g. gw rejected attempt) */ + return dbus_message_new_error(msg, + ERROR_INTERFACE ".AuthenticationRejected", + "Authentication Rejected"); + + case 0x07: /* memory capacity */ + case 0x09: /* connection limit */ + case 0x0a: /* synchronous connection limit */ + case 0x0d: /* limited resources */ + case 0x13: /* user ended the connection */ + case 0x14: /* terminated due to low resources */ + return dbus_message_new_error(msg, + ERROR_INTERFACE ".AuthenticationCanceled", + "Authentication Canceled"); + + case 0x05: /* authentication failure */ + case 0x0E: /* rejected due to security reasons - is this auth failure? */ + case 0x25: /* encryption mode not acceptable - is this auth failure? */ + case 0x26: /* link key cannot be changed - is this auth failure? */ + case 0x29: /* pairing with unit key unsupported - is this auth failure? */ + case 0x2f: /* insufficient security - is this auth failure? */ + default: + return dbus_message_new_error(msg, + ERROR_INTERFACE ".AuthenticationFailed", + "Authentication Failed"); + } +} + +static void bonding_request_free(struct bonding_req *bonding) +{ + struct btd_device *device; + + if (!bonding) + return; + + if (bonding->listener_id) + g_dbus_remove_watch(bonding->conn, bonding->listener_id); - device_set_temporary(device, FALSE); + if (bonding->msg) + dbus_message_unref(bonding->msg); + + if (bonding->conn) + dbus_connection_unref(bonding->conn); + + if (bonding->io_id) + g_source_remove(bonding->io_id); + + if (bonding->io) + g_io_channel_unref(bonding->io); + + device = bonding->device; + + if (device && device->agent) { + agent_destroy(device->agent, FALSE); + device->agent = NULL; + } + + device->bonding = NULL; + g_free(bonding); +} + +static void device_set_paired(struct btd_device *device, gboolean value) +{ + DBusConnection *conn = get_dbus_connection(); emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Paired", - DBUS_TYPE_BOOLEAN, &paired); + DBUS_TYPE_BOOLEAN, &value); +} + +static void device_agent_removed(struct agent *agent, void *user_data) +{ + struct btd_device *device = user_data; + + device_set_agent(device, NULL); + + if (device->authr) + device->authr->agent = NULL; +} + +static struct bonding_req *bonding_request_new(DBusConnection *conn, + DBusMessage *msg, + struct btd_device *device, + const char *agent_path, + uint8_t capability) +{ + struct bonding_req *bonding; + const char *name = dbus_message_get_sender(msg); + struct agent *agent; + + debug("%s: requesting bonding", device->path); + + if (!agent_path) + goto proceed; + + agent = agent_create(device->adapter, name, agent_path, + capability, + device_agent_removed, + device); + if (!agent) { + error("Unable to create a new agent"); + return NULL; + } + + device->agent = agent; + + debug("Temporary agent registered for %s at %s:%s", + device->path, name, agent_path); + +proceed: + + bonding = g_new0(struct bonding_req, 1); + + bonding->conn = dbus_connection_ref(conn); + bonding->msg = dbus_message_ref(msg); + + return bonding; +} + +static gboolean create_bonding_io_cb(GIOChannel *io, GIOCondition cond, + struct btd_device *device) +{ + struct hci_request rq; + auth_requested_cp cp; + evt_cmd_status rp; + struct l2cap_conninfo cinfo; + socklen_t len; + int sk, dd, ret; + + if (!device->bonding) { + /* If we come here it implies a bug somewhere */ + debug("create_bonding_io_cb: no pending bonding!"); + g_io_channel_close(io); + return FALSE; + } + + if (cond & G_IO_NVAL) { + if (device->bonding) { + DBusMessage *reply; + + reply = new_authentication_return(device->bonding->msg, + 0x09); + g_dbus_send_message(device->bonding->conn, reply); + } + + goto cleanup; + } + + if (cond & (G_IO_HUP | G_IO_ERR)) { + debug("Hangup or error on bonding IO channel"); + + if (device->bonding) + error_connection_attempt_failed(device->bonding->conn, + device->bonding->msg, + ENETDOWN); + + goto failed; + } + + sk = g_io_channel_unix_get_fd(io); + + len = sizeof(ret); + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + error("Can't get socket error: %s (%d)", + strerror(errno), errno); + error_failed_errno(device->bonding->conn, device->bonding->msg, + errno); + goto failed; + } + + if (ret != 0) { + if (device->bonding) + error_connection_attempt_failed(device->bonding->conn, + device->bonding->msg, + ret); + goto failed; + } + + len = sizeof(cinfo); + if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &cinfo, &len) < 0) { + error("Can't get connection info: %s (%d)", + strerror(errno), errno); + error_failed_errno(device->bonding->conn, device->bonding->msg, + errno); + goto failed; + } + + dd = hci_open_dev(adapter_get_dev_id(device->adapter)); + if (dd < 0) { + DBusMessage *reply = no_such_adapter(device->bonding->msg); + g_dbus_send_message(device->bonding->conn, reply); + goto failed; + } + + memset(&rp, 0, sizeof(rp)); + + memset(&cp, 0, sizeof(cp)); + cp.handle = htobs(cinfo.hci_handle); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LINK_CTL; + rq.ocf = OCF_AUTH_REQUESTED; + rq.cparam = &cp; + rq.clen = AUTH_REQUESTED_CP_SIZE; + rq.rparam = &rp; + rq.rlen = EVT_CMD_STATUS_SIZE; + rq.event = EVT_CMD_STATUS; + + if (hci_send_req(dd, &rq, HCI_REQ_TIMEOUT) < 0) { + error("Unable to send HCI request: %s (%d)", + strerror(errno), errno); + error_failed_errno(device->bonding->conn, device->bonding->msg, + errno); + hci_close_dev(dd); + goto failed; + } + + if (rp.status) { + error("HCI_Authentication_Requested failed with status 0x%02x", + rp.status); + error_failed_errno(device->bonding->conn, device->bonding->msg, + bt_error(rp.status)); + hci_close_dev(dd); + goto failed; + } + + hci_close_dev(dd); + + device->bonding->io_id = g_io_add_watch(io, + G_IO_NVAL | G_IO_HUP | G_IO_ERR, + (GIOFunc) create_bonding_io_cb, + device); + + return FALSE; + +failed: + g_io_channel_close(io); + +cleanup: + device->bonding->io_id = 0; + bonding_request_free(device->bonding); + + return FALSE; +} + +static void create_bond_req_exit(DBusConnection *conn, void *user_data) +{ + struct btd_device *device = user_data; + + debug("%s: requestor exited before bonding was completed", device->path); + + if (device->authr) + device_cancel_authentication(device); + + if (device->bonding) { + device->bonding->listener_id = 0; + g_io_channel_close(device->bonding->io); + bonding_request_free(device->bonding); + } +} + +DBusMessage *device_create_bonding(struct btd_device *device, + DBusConnection *conn, + DBusMessage *msg, + const char *agent_path, + uint8_t capability) +{ + char filename[PATH_MAX + 1]; + char *str, srcaddr[18], dstaddr[18]; + struct btd_adapter *adapter = device->adapter; + struct bonding_req *bonding; + bdaddr_t src; + int sk; + + adapter_get_address(adapter, &src); + ba2str(&src, srcaddr); + ba2str(&device->bdaddr, dstaddr); + + if (device->bonding) + return in_progress(msg, "Bonding in progress"); + + /* check if a link key already exists */ + create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, + "linkkeys"); + + str = textfile_caseget(filename, dstaddr); + if (str) { + free(str); + return g_dbus_create_error(msg, + ERROR_INTERFACE ".AlreadyExists", + "Bonding already exists"); + } + + sk = l2raw_connect(&src, &device->bdaddr); + if (sk < 0) + return g_dbus_create_error(msg, + ERROR_INTERFACE ".ConnectionAttemptFailed", + "Connection attempt failed"); + + bonding = bonding_request_new(conn, msg, device, agent_path, + capability); + if (!bonding) { + close(sk); + return NULL; + } + + bonding->io = g_io_channel_unix_new(sk); + bonding->io_id = g_io_add_watch(bonding->io, + G_IO_OUT | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + (GIOFunc) create_bonding_io_cb, + device); + + bonding->listener_id = g_dbus_add_disconnect_watch(conn, + dbus_message_get_sender(msg), + create_bond_req_exit, device, + NULL); + + device->bonding = bonding; + bonding->device = device; + + return NULL; +} + +void device_bonding_complete(struct btd_device *device, uint8_t status) +{ + struct bonding_req *bonding = device->bonding; + + if (status) + goto failed; + + device->temporary = FALSE; /* If we were initiators start service discovery immediately. * However if the other end was the initator wait a few seconds @@ -1302,18 +1748,218 @@ int device_set_paired(DBusConnection *conn, struct btd_device *device, device->discov_timer = 0; } - return device_browse(device, bonding->conn, bonding->msg, - NULL, FALSE); + device_browse(device, bonding->conn, bonding->msg, + NULL, FALSE); + } else if (!device->discov_active && !device->discov_timer) { + /* If we are not initiators and there is no currently active + * discovery or discovery timer, set the discovery timer */ + device->discov_timer = g_timeout_add_seconds(DISCOVERY_TIMER, + start_discovery, + device); } - /* If we are not initiators and there is no currently active discovery - * or discovery timer, set the discovery timer */ - if (!device->discov_active && !device->discov_timer) - device->discov_timer = g_timeout_add_seconds(DISCOVERY_TIMER, - start_discovery, - device); + device_set_paired(device, TRUE); - return 0; + g_free(device->authr); + device->authr = NULL; + bonding_request_free(bonding); + + return; + +failed: + + device_cancel_bonding(device, status); +} + +gboolean device_is_bonding(struct btd_device *device, const char *sender) +{ + struct bonding_req *bonding = device->bonding; + + if (!device->bonding) + return FALSE; + + if (!sender) + return TRUE; + + return g_str_equal(sender, dbus_message_get_sender(bonding->msg)); +} + +void device_cancel_bonding(struct btd_device *device, uint8_t status) +{ + struct bonding_req *bonding = device->bonding; + DBusMessage *reply; + + if (!bonding) + return; + + debug("%s: canceling bonding request", device->path); + + if (device->authr) + device_cancel_authentication(device); + + reply = new_authentication_return(bonding->msg, status); + g_dbus_send_message(bonding->conn, reply); + + if (device->bonding->io_id) + g_source_remove(device->bonding->io_id); + + bonding_request_free(bonding); +} + +static void pincode_cb(struct agent *agent, DBusError *err, const char *pincode, + void *data) +{ + struct authentication_req *auth = data; + struct btd_device *device = auth->device; + + /* No need to reply anything if the authentication already failed */ + if (!auth->cb) + return; + + ((agent_pincode_cb) auth->cb)(agent, err, pincode, device); + + auth->cb = NULL; +} + +static void confirm_cb(struct agent *agent, DBusError *err, void *data) +{ + struct authentication_req *auth = data; + struct btd_device *device = auth->device; + + /* No need to reply anything if the authentication already failed */ + if (!auth->cb) + return; + + ((agent_cb) auth->cb)(agent, err, device); + + auth->cb = NULL; +} + +static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey, + void *data) +{ + struct authentication_req *auth = data; + struct btd_device *device = auth->device; + + /* No need to reply anything if the authentication already failed */ + if (!auth->cb) + return; + + ((agent_passkey_cb) auth->cb)(agent, err, passkey, device); + + auth->cb = NULL; +} + +int device_request_authentication(struct btd_device *device, auth_type_t type, + uint32_t passkey, void *cb) +{ + struct authentication_req *auth; + struct agent *agent; + int ret; + + debug("%s: requesting agent authentication", device->path); + + agent = device->agent; + + if (!agent) + agent = adapter_get_agent(device->adapter); + + if (!agent) { + error("No agent available for %u request", type); + return -EPERM; + } + + auth = g_new0(struct authentication_req, 1); + auth->agent = agent; + auth->device = device; + auth->cb = cb; + auth->type = type; + device->authr = auth; + + switch (type) { + case AUTH_TYPE_PINCODE: + ret = agent_request_pincode(agent, device, pincode_cb, + auth); + break; + case AUTH_TYPE_PASSKEY: + ret = agent_request_passkey(agent, device, passkey_cb, + auth); + break; + case AUTH_TYPE_CONFIRM: + ret = agent_request_confirmation(agent, device, passkey, + confirm_cb, auth); + break; + case AUTH_TYPE_NOTIFY: + ret = agent_display_passkey(agent, device, passkey); + break; + case AUTH_TYPE_AUTO: + ret = 0; + break; + default: + ret = -EINVAL; + } + + if (ret < 0) { + error("Failed requesting authentication"); + g_free(auth); + device->authr = NULL; + } + + return ret; +} + +static void cancel_authentication(struct authentication_req *auth) +{ + struct btd_device *device = auth->device; + struct agent *agent = auth->agent; + DBusError err; + + if (!auth->cb) + return; + + dbus_error_init(&err); + dbus_set_error_const(&err, "org.bluez.Error.Canceled", NULL); + + switch (auth->type) { + case AUTH_TYPE_PINCODE: + ((agent_pincode_cb) auth->cb)(agent, &err, NULL, device); + break; + case AUTH_TYPE_CONFIRM: + ((agent_cb) auth->cb)(agent, &err, device); + break; + case AUTH_TYPE_PASSKEY: + ((agent_passkey_cb) auth->cb)(agent, &err, 0, device); + break; + case AUTH_TYPE_NOTIFY: + case AUTH_TYPE_AUTO: + /* User Notify/Auto doesn't require any reply */ + break; + } + + dbus_error_free(&err); + auth->cb = NULL; +} + +void device_cancel_authentication(struct btd_device *device) +{ + struct authentication_req *auth = device->authr; + + if (!auth) + return; + + debug("%s: canceling authentication request", device->path); + + if (auth->agent) + agent_cancel(auth->agent); + + cancel_authentication(auth); + device->authr = NULL; + g_free(auth); +} + +gboolean device_is_authenticating(struct btd_device *device) +{ + return (device->authr != NULL); } void btd_device_add_uuid(struct btd_device *device, const char *uuid) diff --git a/src/device.h b/src/device.h index 4113ecdb..c9137f7c 100644 --- a/src/device.h +++ b/src/device.h @@ -24,9 +24,19 @@ #define DEVICE_INTERFACE "org.bluez.Device" +struct btd_device; + +typedef enum { + AUTH_TYPE_PINCODE, + AUTH_TYPE_PASSKEY, + AUTH_TYPE_CONFIRM, + AUTH_TYPE_NOTIFY, + AUTH_TYPE_AUTO, +} auth_type_t; + struct btd_device *device_create(DBusConnection *conn, struct btd_adapter *adapter, const gchar *address); -void device_remove(DBusConnection *conn, struct btd_device *device); +void device_remove(struct btd_device *device, DBusConnection *conn); gint device_address_cmp(struct btd_device *device, const gchar *address); int device_browse(struct btd_device *device, DBusConnection *conn, DBusMessage *msg, uuid_t *search, gboolean reverse); @@ -45,12 +55,21 @@ void device_set_temporary(struct btd_device *device, gboolean temporary); void device_set_cap(struct btd_device *device, uint8_t cap); void device_set_auth(struct btd_device *device, uint8_t auth); uint8_t device_get_auth(struct btd_device *device); -int device_set_paired(DBusConnection *conn, struct btd_device *device, - struct bonding_request_info *bonding); gboolean device_get_connected(struct btd_device *device); -void device_set_connected(DBusConnection *conn, struct btd_device *device, +void device_set_connected(struct btd_device *device, DBusConnection *conn, gboolean connected); void device_set_secmode3_conn(struct btd_device *device, gboolean enable); +DBusMessage *device_create_bonding(struct btd_device *device, + DBusConnection *conn, DBusMessage *msg, + const char *agent_path, uint8_t capability); +void device_remove_bondind(struct btd_device *device, DBusConnection *connection); +void device_bonding_complete(struct btd_device *device, uint8_t status); +gboolean device_is_bonding(struct btd_device *device, const char *sender); +void device_cancel_bonding(struct btd_device *device, uint8_t status); +int device_request_authentication(struct btd_device *device, auth_type_t type, + uint32_t passkey, void *cb); +void device_cancel_authentication(struct btd_device *device); +gboolean device_is_authenticating(struct btd_device *device); #define BTD_UUIDS(args...) ((const char *[]) { args, NULL } ) |