diff options
| -rw-r--r-- | hcid/Makefile.am | 2 | ||||
| -rw-r--r-- | hcid/adapter.c | 56 | ||||
| -rw-r--r-- | hcid/adapter.h | 3 | ||||
| -rw-r--r-- | hcid/agent.c | 587 | ||||
| -rw-r--r-- | hcid/agent.h | 58 | 
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); + | 
