summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hcid/Makefile.am2
-rw-r--r--hcid/adapter.c56
-rw-r--r--hcid/adapter.h3
-rw-r--r--hcid/agent.c587
-rw-r--r--hcid/agent.h58
5 files changed, 698 insertions, 8 deletions
diff --git a/hcid/Makefile.am b/hcid/Makefile.am
index dfa29b82..dda695b6 100644
--- a/hcid/Makefile.am
+++ b/hcid/Makefile.am
@@ -21,7 +21,7 @@ libhciserver_a_SOURCES = hcid.h security.c storage.c \
dbus-database.c dbus-database.h dbus-security.c dbus-security.h \
dbus-service.c dbus-service.h dbus-test.c dbus-test.h \
dbus-hci.h dbus-hci.c dbus-sdp.c dbus-sdp.h \
- telephony.h telephony.c
+ telephony.h telephony.c agent.h agent.c
if HCID
sbin_PROGRAMS = hcid
diff --git a/hcid/adapter.c b/hcid/adapter.c
index e26e2af6..0b1a3c9f 100644
--- a/hcid/adapter.c
+++ b/hcid/adapter.c
@@ -64,6 +64,7 @@
#include "error.h"
#include "glib-helper.h"
#include "logging.h"
+#include "agent.h"
#define NUM_ELEMENTS(table) (sizeof(table)/sizeof(const char *))
@@ -3621,33 +3622,76 @@ static DBusHandlerResult find_device(DBusConnection *conn,
return send_message_and_unref(conn, reply);
}
+static void agent_exited(const char *name, struct adapter *adapter)
+{
+ debug("Agent %s exited without calling Unregister", name);
+
+ agent_destroy(adapter->agent, TRUE);
+
+ adapter->agent = NULL;
+}
+
+static void agent_removed(struct agent *agent, struct adapter *adapter)
+{
+ if (adapter->agent == agent)
+ adapter->agent = NULL;
+}
+
static DBusHandlerResult register_agent(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- char *agent_path;
+ const char *path, *name;
+ struct agent *agent;
+ struct adapter *adapter = data;
if (!hcid_dbus_use_experimental())
return error_unknown_method(conn, msg);
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH,
- &agent_path, DBUS_TYPE_INVALID))
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
return error_invalid_arguments(conn, msg, NULL);
+ if (adapter->agent)
+ return error_already_exists(conn, msg, "Agent already exists");
+
+ name = dbus_message_get_sender(msg);
+
+ agent = agent_create(name, path, NULL,
+ (agent_remove_cb) agent_removed, adapter);
+ if (!agent)
+ return error_failed(conn, msg, "Failed to create a new agent");
+
+ adapter->agent = agent;
+
+ name_listener_add(conn, name, (name_cb_t) agent_exited, adapter);
+
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult unregister_agent(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- char *agent_path;
+ const char *path, *name;
+ struct adapter *adapter = data;
if (!hcid_dbus_use_experimental())
return error_unknown_method(conn, msg);
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH,
- &agent_path, DBUS_TYPE_INVALID))
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
return error_invalid_arguments(conn, msg, NULL);
+ name = dbus_message_get_sender(msg);
+
+ if (!adapter->agent || !agent_matches(adapter->agent, name, path))
+ return error_does_not_exist(conn, msg, "No such agent");
+
+ name_listener_remove(conn, name, (name_cb_t) agent_exited,
+ adapter);
+
+ agent_destroy(adapter->agent, FALSE);
+ adapter->agent = NULL;
+
return DBUS_HANDLER_RESULT_HANDLED;
}
diff --git a/hcid/adapter.h b/hcid/adapter.h
index 1ea266d5..a09dd37e 100644
--- a/hcid/adapter.h
+++ b/hcid/adapter.h
@@ -108,7 +108,8 @@ struct adapter {
char *discov_requestor; /* discovery requestor unique name */
DBusMessage *discovery_cancel; /* discovery cancel message request */
GSList *passkey_agents;
- GSList *auth_agents; /* Authorization agents */
+ GSList *auth_agents; /* Authorization agents */
+ struct agent *agent; /* For the new API */
bdaddr_t agents_disabled; /* temporarely disable agents for bda */
GSList *active_conn;
struct bonding_request_info *bonding;
diff --git a/hcid/agent.c b/hcid/agent.c
new file mode 100644
index 00000000..fa7b3984
--- /dev/null
+++ b/hcid/agent.c
@@ -0,0 +1,587 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "dbus.h"
+#include "dbus-helper.h"
+#include "hcid.h"
+#include "dbus-common.h"
+#include "dbus-service.h"
+#include "dbus-error.h"
+#include "error.h"
+#include "adapter.h"
+#include "dbus-hci.h"
+#include "agent.h"
+
+#define REQUEST_TIMEOUT (60 * 1000) /* 60 seconds */
+#define AGENT_TIMEOUT (10 * 60 * 1000) /* 10 minutes */
+
+typedef enum {
+ AGENT_REQUEST_PASSKEY,
+ AGENT_REQUEST_AUTHORIZE,
+ AGENT_REQUEST_CONFIRM_MODE
+} agent_request_type_t;
+
+struct agent {
+ struct adapter *adapter;
+ char *addr;
+ char *name;
+ char *path;
+ struct agent_request *request;
+ int exited;
+ guint timeout;
+ agent_remove_cb remove_cb;
+ void *remove_cb_data;
+};
+
+struct agent_request {
+ agent_request_type_t type;
+ struct agent *agent;
+ char *device;
+ char *uuid;
+ char remote_address[18];
+ DBusPendingCall *call;
+ void *cb;
+ void *user_data;
+};
+
+static DBusConnection *connection = NULL;
+
+static void release_agent(struct agent *agent);
+static void send_cancel_request(struct agent_request *req);
+
+static void agent_free(struct agent *agent)
+{
+ if (!agent)
+ return;
+
+ if (agent->request) {
+ DBusError err;
+
+ if (agent->request->type == AGENT_REQUEST_PASSKEY) {
+ agent_passkey_cb cb = agent->request->cb;
+ cb(agent, &err, NULL, agent->request->user_data);
+ } else {
+ agent_cb cb = agent->request->cb;
+ cb(agent, &err, agent->request->user_data);
+ }
+
+ send_cancel_request(agent->request);
+ }
+
+ if (agent->timeout)
+ g_source_remove(agent->timeout);
+
+ if (!agent->exited)
+ release_agent(agent);
+
+ g_free(agent->name);
+ g_free(agent->path);
+ g_free(agent->addr);
+
+ g_free(agent);
+}
+
+static gboolean agent_timeout(struct agent *agent)
+{
+ debug("Agent at %s, %s timed out", agent->name, agent->path);
+
+ agent->timeout = 0;
+
+ if (agent->remove_cb)
+ agent->remove_cb(agent, agent->remove_cb_data);
+
+ agent_free(agent);
+
+ return FALSE;
+}
+
+struct agent *agent_create(const char *name, const char *path,
+ const char *address,
+ agent_remove_cb cb, void *remove_cb_data)
+{
+ struct agent *agent;
+
+ agent = g_new0(struct agent, 1);
+
+ agent->name = g_strdup(name);
+ agent->path = g_strdup(path);
+ agent->remove_cb = cb;
+ agent->remove_cb_data = remove_cb_data;
+
+ if (address) {
+ agent->addr = g_strdup(address);
+ agent->timeout = g_timeout_add(AGENT_TIMEOUT,
+ (GSourceFunc) agent_timeout, agent);
+ }
+
+ return agent;
+}
+
+int agent_destroy(struct agent *agent, gboolean exited)
+{
+ agent->exited = exited;
+ agent_free(agent);
+ return 0;
+}
+
+static struct agent_request *agent_request_new(struct agent *agent,
+ const char *device,
+ void *cb,
+ void *user_data)
+{
+ struct agent_request *req;
+
+ req = g_new0(struct agent_request, 1);
+
+ req->agent = agent;
+ req->device = g_strdup(device);
+ req->cb = cb;
+ req->user_data = user_data;
+
+ return req;
+}
+
+static void agent_request_free(struct agent_request *req)
+{
+ g_free(req->device);
+ g_free(req->uuid);
+ if (req->call)
+ dbus_pending_call_unref(req->call);
+ g_free(req);
+}
+
+static void agent_call_cancel(struct agent_request *req)
+{
+ struct agent *agent = req->agent;
+ DBusMessage *message;
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "Cancel");
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ dbus_message_set_no_reply(message, TRUE);
+ send_message_and_unref(connection, message);
+}
+
+static void authorize_reply(DBusPendingCall *call, void *data)
+{
+ struct agent_request *req = data;
+ struct agent *agent = req->agent;
+ agent_cb cb = req->cb;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError err;
+
+ debug("authorize reply");
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY))
+ agent_call_cancel(req);
+ error("Authorization agent replied with an error: %s, %s",
+ err.name, err.message);
+ cb(agent, &err, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
+ error("Wrong authorization agent reply signature: %s",
+ err.message);
+ cb(agent, &err, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ debug("successfull reply was sent");
+
+ cb(agent, NULL, req->user_data);
+
+done:
+ dbus_message_unref(reply);
+
+ agent_request_free(req);
+ agent->request = NULL;
+
+ debug("auth_agent_reply: returning");
+}
+
+static DBusPendingCall *agent_call_authorize(struct agent *agent,
+ const char *device_path,
+ const char *uuid)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "Authorize");
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return NULL;
+ }
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_OBJECT_PATH, &uuid,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ dbus_message_unref(message);
+ return NULL;
+ }
+
+ dbus_message_unref(message);
+ return call;
+}
+
+int agent_authorize(struct agent *agent,
+ const char *device,
+ const char *uuid,
+ agent_cb cb,
+ void *user_data)
+{
+ struct agent_request *req;
+
+ debug("agent_authorize");
+
+ req = agent_request_new(agent, device, cb, user_data);
+
+ req->call = agent_call_authorize(agent, device, uuid);
+ if (!req->call) {
+ agent_request_free(req);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_pending_call_set_notify(req->call, authorize_reply, req,
+ NULL);
+ agent->request = req;
+
+ debug("authorize request was sent for %s", device);
+
+ return 0;
+}
+
+static DBusPendingCall *passkey_request_new(const char *device,
+ struct agent *agent,
+ dbus_bool_t numeric)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "PasskeyRequest");
+ if (message == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return NULL;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &device,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ dbus_message_unref(message);
+ return NULL;
+ }
+
+ dbus_message_unref(message);
+ return call;
+}
+
+static void passkey_reply(DBusPendingCall *call, void *user_data)
+{
+ struct agent_request *req = user_data;
+ struct agent *agent = req->agent;
+ struct adapter *adapter = agent->adapter;
+ agent_passkey_cb cb = req->cb;
+ DBusMessage *message;
+ DBusError err;
+ bdaddr_t sba;
+ size_t len;
+ char *pin;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ message = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+ error("Agent replied with an error: %s, %s",
+ err.name, err.message);
+
+ cb(agent, &err, NULL, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(message, &err,
+ DBUS_TYPE_STRING, &pin,
+ DBUS_TYPE_INVALID)) {
+ error("Wrong passkey reply signature: %s", err.message);
+ cb(agent, &err, NULL, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ len = strlen(pin);
+
+ dbus_error_init(&err);
+ if (len > 16 || len < 1) {
+ error("Invalid passkey length from handler");
+ dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
+ "Invalid passkey length");
+ cb(agent, &err, NULL, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ str2ba(adapter->address, &sba);
+
+ set_pin_length(&sba, len);
+
+ cb(agent, NULL, pin, req->user_data);
+
+done:
+ if (message)
+ dbus_message_unref(message);
+
+ dbus_pending_call_cancel(req->call);
+ if (req->call)
+ dbus_pending_call_unref(req->call);
+ g_free(req);
+
+ if (agent->addr) {
+ if (agent->remove_cb)
+ agent->remove_cb(agent, agent->remove_cb_data);
+ agent_free(agent);
+ }
+}
+
+int agent_request_passkey(struct agent *agent, const char *device,
+ agent_passkey_cb cb, void *user_data)
+{
+ struct agent_request *req;
+
+ req = agent_request_new(agent, device, cb, user_data);
+
+ req->call = passkey_request_new(device, agent, FALSE);
+ if (!req->call)
+ goto failed;
+
+ dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ g_free(req);
+ return -1;
+}
+
+static DBusPendingCall *confirm_request_new(struct agent *agent,
+ const char *device,
+ const char *value)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "Confirm");
+ if (message == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return NULL;
+ }
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_OBJECT_PATH, &device,
+ DBUS_TYPE_STRING, &value,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ dbus_message_unref(message);
+ return NULL;
+ }
+
+ dbus_message_unref(message);
+
+ return call;
+}
+
+static void confirm_reply(DBusPendingCall *call, void *user_data)
+{
+ struct agent_request *req = user_data;
+ struct agent *agent = req->agent;
+ DBusMessage *message;
+ DBusError err;
+ agent_cb cb = req->cb;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ message = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+
+ error("Agent replied with an error: %s, %s",
+ err.name, err.message);
+
+ cb(agent, &err, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+ error("Wrong confirm reply signature: %s", err.message);
+ cb(agent, &err, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ cb(agent, NULL, req->user_data);
+done:
+ if (message)
+ dbus_message_unref(message);
+
+ agent->request = NULL;
+ dbus_pending_call_cancel(req->call);
+ if (req->call)
+ dbus_pending_call_unref(req->call);
+ g_free(req);
+
+ if (agent->addr) {
+ if (agent->remove_cb)
+ agent->remove_cb(agent, agent->remove_cb_data);
+ agent_free(agent);
+ }
+}
+
+int agent_confirm(struct agent *agent, const char *device, const char *pin,
+ agent_cb cb, void *user_data)
+{
+ struct agent_request *req;
+
+ debug("Calling Agent.Confirm: name=%s, path=%s",
+ agent->name, agent->path);
+
+ req = agent_request_new(agent, device, cb, user_data);
+
+ req->call = confirm_request_new(agent, device, pin);
+ if (!req->call)
+ goto failed;
+
+ dbus_pending_call_set_notify(req->call, confirm_reply, req, NULL);
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ agent_request_free(req);
+ return -1;
+}
+
+static void send_cancel_request(struct agent_request *req)
+{
+ DBusMessage *message;
+
+ message = dbus_message_new_method_call(req->agent->name, req->agent->path,
+ "org.bluez.Agent", "Cancel");
+ if (message == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ dbus_message_set_no_reply(message, TRUE);
+
+ send_message_and_unref(connection, message);
+
+ dbus_pending_call_cancel(req->call);
+ agent_request_free(req);
+}
+
+void release_agent(struct agent *agent)
+{
+ DBusMessage *message;
+
+ debug("Releasing agent %s, %s", agent->name, agent->path);
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "Release");
+ if (message == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ dbus_message_set_no_reply(message, TRUE);
+
+ send_message_and_unref(connection, message);
+}
+
+gboolean agent_matches(struct agent *agent, const char *name, const char *path)
+{
+ if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
+ return TRUE;
+
+ return FALSE;
+}
+
+void agent_exit(void)
+{
+ dbus_connection_unref(connection);
+}
+
+void agent_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+}
diff --git a/hcid/agent.h b/hcid/agent.h
new file mode 100644
index 00000000..8f26f37c
--- /dev/null
+++ b/hcid/agent.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct agent;
+
+typedef void (*agent_cb) (struct agent *agent, DBusError *err,
+ void *user_data);
+
+typedef void (*agent_passkey_cb) (struct agent *agent, DBusError *err,
+ const char *passkey, void *user_data);
+
+typedef void (*agent_remove_cb) (struct agent *agent, void *user_data);
+
+struct agent *agent_create(const char *name, const char *path,
+ const char *address,
+ agent_remove_cb cb, void *remove_cb_data);
+
+int agent_destroy(struct agent *agent, gboolean exited);
+
+int agent_authorize(struct agent *agent, const char *device, const char *uuid,
+ agent_cb cb, void *user_data);
+
+int agent_request_passkey(struct agent *agent, const char *device,
+ agent_passkey_cb cb, void *user_data);
+
+int agent_confirm(struct agent *agent, const char *device, const char *pin,
+ agent_cb cb, void *user_data);
+
+int agent_cancel(struct agent *agent);
+
+int agent_release(struct agent *agent);
+
+gboolean agent_matches(struct agent *agent, const char *name, const char *path);
+
+void agent_init(void);
+void agent_exit(void);
+