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