diff options
| -rw-r--r-- | hcid/dbus-adapter.c | 194 | ||||
| -rw-r--r-- | hcid/dbus.c | 202 | ||||
| -rw-r--r-- | hcid/dbus.h | 12 | ||||
| -rw-r--r-- | hcid/hcid.h | 34 | ||||
| -rw-r--r-- | hcid/security.c | 4 | 
5 files changed, 358 insertions, 88 deletions
| diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index 2ff247de..fffb6fd4 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -73,6 +73,62 @@ static const char *phone_minor_cls[] = {  	"isdn"  }; +int find_connection_handle(int dd, bdaddr_t *peer) +{ +	struct hci_conn_info_req *cr; +	int handle = -1; + +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) +		return -1; + +	bacpy(&cr->bdaddr, peer); +	cr->type = ACL_LINK; + +	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { +		free(cr); +		return -1; +	} + +	handle = cr->conn_info->handle; +	free(cr); + +	return handle; +} + +static int bonding_requests_find(const void *a, const void *b) +{ +	const struct bonding_request_info *dev = a; +	const bdaddr_t *peer = b; + +	if (!dev) +		return -1; + +	if (memcmp(dev->addr, peer, sizeof(*peer)) == 0) { +		return 0; +	} + +	return -1; +} + +static int bonding_requests_append(struct slist **list, bdaddr_t *addr, DBusMessage *msg, bonding_state_t bonding_state) +{ +	struct bonding_request_info *data; + +	data = malloc(sizeof(*data)); +	if (!data) +		return -1; + +	data->addr = malloc(sizeof(*data->addr)); +	memcpy(data->addr, addr, sizeof(*data->addr)); +	data->msg = msg; +	data->bonding_state = bonding_state; + +	*list = slist_append(*list, data); + +	return 0; +} +  static DBusHandlerResult handle_dev_get_address_req(DBusConnection *conn, DBusMessage *msg, void *data)  {  	struct hci_dbus_data *dbus_data = data; @@ -888,75 +944,125 @@ static DBusHandlerResult handle_dev_last_used_req(DBusConnection *conn, DBusMess  static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data)  { +	char filename[PATH_MAX + 1]; +	char local_addr[18];  	struct hci_request rq; -	auth_requested_cp cp;  	evt_cmd_status rp; -	DBusMessage *reply; -	char *str_bdaddr; +	DBusError err; +	char *peer_addr; +	char *str;  	struct hci_dbus_data *dbus_data = data; -	struct hci_conn_info_req *cr; -	bdaddr_t bdaddr; -	int dd, dev_id; - -	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str_bdaddr, -							DBUS_TYPE_INVALID); - -	str2ba(str_bdaddr, &bdaddr); +	struct slist *tmp_list; +	bdaddr_t peer_bdaddr; +	int dd, handle; +	bonding_state_t bonding_state; + +	dbus_error_init(&err); +	dbus_message_get_args(msg, &err, +			      	DBUS_TYPE_STRING, &peer_addr, +				DBUS_TYPE_INVALID); + +	if (dbus_error_is_set(&err)) { +		error("Can't extract message arguments:%s", err.message); +		dbus_error_free(&err); +		return error_invalid_arguments(conn, msg); +	} -	dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); +	str2ba(peer_addr, &peer_bdaddr); +	 +	/* check if there is a pending bonding request */ +	tmp_list = slist_find(dbus_data->bonding_requests, &peer_bdaddr, bonding_requests_find); -	if (dev_id < 0) -		return error_failed(conn, msg, ENOTCONN); +	if (tmp_list) +		return error_bonding_in_progress(conn, msg); -	if (dbus_data->dev_id != dev_id) -		return error_failed(conn, msg, ENOTCONN); +	get_device_address(dbus_data->dev_id, local_addr, sizeof(local_addr)); -	dd = hci_open_dev(dev_id); -	if (dd < 0) -		return error_no_such_adapter(conn, msg); +	/* check if a link key already exists */ +	snprintf(filename, PATH_MAX, "%s/%s/linkkeys", STORAGEDIR, local_addr); -	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); -	if (!cr) { -		hci_close_dev(dd); -		return error_out_of_memory(conn, msg); +	str = textfile_get(filename, peer_addr); +	if (str) { +		free(str); +		return error_bonding_already_exists(conn, msg);  	} -	bacpy(&cr->bdaddr, &bdaddr); -	cr->type = ACL_LINK; +	/* check if the address belongs to the last seen cache */ +	snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, local_addr); +	str = textfile_get(filename, peer_addr); +	if (!str) +		return error_unknown_address(conn, msg); -	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { -		hci_close_dev(dd); -		free(cr); -		return error_failed(conn, msg, errno); -	} +	free(str); -	memset(&cp, 0, sizeof(cp)); -	cp.handle = cr->conn_info->handle; +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) +		return error_no_such_adapter(conn, msg); + +	/* check if there is an active connection */ +	//dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &peer_bdaddr); +	handle = find_connection_handle(dd, &peer_bdaddr);  	memset(&rq, 0, sizeof(rq)); +	memset(&rp, 0, sizeof(rp)); +  	rq.ogf    = OGF_LINK_CTL; -	rq.ocf    = OCF_AUTH_REQUESTED; -	rq.cparam = &cp; -	rq.clen   = AUTH_REQUESTED_CP_SIZE; +	rq.event = EVT_CMD_STATUS;  	rq.rparam = &rp; -	rq.rlen   = EVT_CMD_STATUS_SIZE; -	rq.event  = EVT_CMD_STATUS; +	rq.rlen = EVT_CMD_STATUS_SIZE; + +	if (handle < 0 ) { +		create_conn_cp cp; + +		memset(&cp, 0, sizeof(cp)); +		/* create a new connection */ +		bonding_state = CONNECTING; +		 +		bacpy(&cp.bdaddr, &peer_bdaddr); +		cp.pkt_type = htobs(HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5); +		cp.pscan_rep_mode = 0x02; +		cp.clock_offset = htobs(0x0000); +		cp.role_switch = 0x01; + +		rq.ocf = OCF_CREATE_CONN; +		rq.cparam = &cp; +		rq.clen = CREATE_CONN_CP_SIZE; +	} else { +		/* connection found */ +		auth_requested_cp cp; + +		memset(&cp, 0, sizeof(cp)); + +		/* active connection found */ +		bonding_state = PAIRING; + +		cp.handle = handle; + +		rq.ocf    = OCF_AUTH_REQUESTED; +		rq.cparam = &cp; +		rq.clen   = AUTH_REQUESTED_CP_SIZE; +	}  	if (hci_send_req(dd, &rq, 100) < 0) { -		error("Unable to send authentication request: %s (%d)", -							strerror(errno), errno); +		error("Unable to send the HCI request: %s (%d)", +				strerror(errno), errno);  		hci_close_dev(dd); -		free(cr);  		return error_failed(conn, msg, errno);  	} -	reply = dbus_message_new_method_return(msg); +	if (rp.status) { +		error("Failed with status 0x%02x", rp.status); +		hci_close_dev(dd); +		return error_failed(conn, msg, rp.status); +	} -	free(cr); +	/* add in the bonding requests list */ +	bonding_requests_append(&dbus_data->bonding_requests, &peer_bdaddr, msg, bonding_state); -	close(dd); +	dbus_message_ref(msg); +	hci_close_dev(dd); -	return send_reply_and_unref(conn, reply); +	return DBUS_HANDLER_RESULT_HANDLED;  }  static DBusHandlerResult handle_dev_remove_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data) diff --git a/hcid/dbus.c b/hcid/dbus.c index 54bc54b4..64f0ad18 100644 --- a/hcid/dbus.c +++ b/hcid/dbus.c @@ -101,6 +101,7 @@ static const char *phone_minor_cls[] = {  	"isdn"  }; +  void discovered_device_free(void *data, void *user_data)  {  	struct discovered_dev_info *dev = data; @@ -179,6 +180,63 @@ static int remote_name_remove(struct slist **list, bdaddr_t *addr)  	return ret_val;  } +static int bonding_requests_find(const void *a, const void *b) +{ +	const struct bonding_request_info *dev = a; +	const bdaddr_t *peer = b; + +	if (!dev) +		return -1; + +	if (memcmp(dev->addr, peer, sizeof(*peer)) == 0) +		return 0; + +	return -1; +} + +static DBusMessage *dbus_msg_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".RepeatedAttemps", +						  	"Repeated Attempts"); + +	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 0x14: /* terminated due to low resources */ +		return dbus_message_new_error(msg, ERROR_INTERFACE".AuthenticationCanceled", +						  	"Authentication Canceled"); + +	case 0x05: /* authentication failure */ +	case 0x06: /* pin missing */ +	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"); +	} +} +  /*   * Timeout functions Protypes   */ @@ -463,7 +521,9 @@ void hcid_dbus_request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci)  void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const uint8_t status)  { -	DBusMessage *message = NULL; +	struct hci_dbus_data *pdata; +	DBusMessage *msg_reply = NULL; +	DBusMessage *msg_signal = NULL;  	char *local_addr, *peer_addr;  	const char *name;  	bdaddr_t tmp; @@ -486,28 +546,53 @@ void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const u  	 * 0x01-0x0F: authentication request failed  	 */  	name = status ? "BondingFailed" : "BondingCreated"; +	/* authentication signal */ +	msg_signal = dbus_message_new_signal(path, ADAPTER_INTERFACE, name); -	message = dbus_message_new_signal(path, ADAPTER_INTERFACE, name); - -	if (message == NULL) { +	if (msg_signal == NULL) {  		error("Can't allocate D-Bus remote name message");  		goto failed;  	} -	dbus_message_append_args(message, +	dbus_message_append_args(msg_signal,  					DBUS_TYPE_STRING, &peer_addr,  					DBUS_TYPE_INVALID); -	if (dbus_connection_send(connection, message, NULL) == FALSE) { -		error("Can't send D-Bus remote name message"); +	if (dbus_connection_send(connection, msg_signal, NULL) == FALSE) { +		error("Can't send D-Bus bonding created signal");  		goto failed;  	}  	dbus_connection_flush(connection); +	/* create the authentication reply */ +	if (dbus_connection_get_object_path_data(connection, path, (void *) &pdata)) { +		struct slist *l; + +		l = slist_find(pdata->bonding_requests, peer, bonding_requests_find); + +		if (l) { +			struct bonding_request_info *dev = l->data; + +			msg_reply = dbus_msg_new_authentication_return(dev->msg, status); +			if (dbus_connection_send(connection, msg_reply, NULL) == FALSE) { +				error("Can't send D-Bus reply for create bonding request"); +				goto failed; +			} + +			dbus_message_unref(dev->msg); +			pdata->bonding_requests = slist_remove(pdata->bonding_requests, dev); +			free(dev->addr); +			free(dev); +		} +	} +  failed: -	if (message) -		dbus_message_unref(message); +	if (msg_signal) +		dbus_message_unref(msg_signal); + +	if (msg_reply) +		dbus_message_unref(msg_reply);  	bt_free(local_addr);  	bt_free(peer_addr); @@ -877,8 +962,105 @@ failed:  	bt_free(peer_addr);  } -void hcid_dbus_conn_complete(bdaddr_t *local, bdaddr_t *peer) +void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, bdaddr_t *peer)  { +	char path[MAX_PATH_LENGTH]; +	struct hci_request rq; +	evt_cmd_status rp; +	auth_requested_cp cp; +	struct hci_dbus_data *pdata = NULL; +	const struct slist *l; +	struct bonding_request_info *dev = NULL; +	char *local_addr, *peer_addr; +	bdaddr_t tmp; +	int dd, id; + +	baswap(&tmp, local); local_addr = batostr(&tmp); +	baswap(&tmp, peer); peer_addr = batostr(&tmp); + +	id = hci_devid(local_addr); +	if (id < 0) { +		error("No matching device id for %s", local_addr); +		goto failed; +	} + +	snprintf(path, sizeof(path), "%s/hci%d", ADAPTER_PATH, id); + +	if (!dbus_connection_get_object_path_data(connection, path, (void *) &pdata)) +		goto failed; + +	l = slist_find(pdata->bonding_requests, peer, bonding_requests_find); + +	/*  +	 * Connections can be requested by other applications,  profiles and bonding +	 * For now it's necessary check only if there a pending bonding request +	 */ +	if (!l)  +		goto failed; + +	dev = l->data; + +	/* connection failed */	 +	if (status) { +		error_connection_attempt_failed(connection, dev->msg, status); +		goto failed; +	} + +	if (dev->bonding_state != CONNECTING) +		goto failed; /* FIXME: is it possible? */ + +	dd = hci_open_dev(pdata->dev_id); +	if (dd < 0) { +		error_no_such_adapter(connection, dev->msg); +		goto failed; +	} + +	/* request authentication */ +	memset(&rq, 0, sizeof(rq)); +	memset(&rp, 0, sizeof(rp)); +	memset(&cp, 0, sizeof(cp)); + +	cp.handle = handle; + +	rq.ogf    = OGF_LINK_CTL; +	rq.event  = EVT_CMD_STATUS; +	rq.rparam = &rp; +	rq.rlen   = EVT_CMD_STATUS_SIZE; + +	rq.ocf    = OCF_AUTH_REQUESTED; +	rq.cparam = &cp; +	rq.clen   = AUTH_REQUESTED_CP_SIZE; + +	if (hci_send_req(dd, &rq, 100) < 0) { +		error("Unable to send the HCI request: %s (%d)", +			strerror(errno), errno); +		error_failed(connection, dev->msg, errno); +		goto failed; +	} + +	if (rp.status) { +		error("Failed with status 0x%02x", rp.status); +		error_failed(connection, dev->msg, rp.status); +		goto failed; +	} +	/* request sent properly */ +	dev->bonding_state = PAIRING; + +failed: +	/* remove from the list if the HCI pairing request was not sent */ +	if (dev) { +		if (dev->bonding_state != PAIRING) { +			dbus_message_unref(dev->msg); +			pdata->bonding_requests = slist_remove(pdata->bonding_requests, dev); +			free(dev->addr); +			free(dev); +		} +	} + +	hci_close_dev(dd); + +	bt_free(local_addr); +	bt_free(peer_addr);  }  void hcid_dbus_disconn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t reason) diff --git a/hcid/dbus.h b/hcid/dbus.h index 7635b122..71d436b8 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -89,6 +89,17 @@ struct discovered_dev_info {  	name_status_t name_status;  }; +typedef enum { +	CONNECTING, +	PAIRING	 +} bonding_state_t; + +struct bonding_request_info { +	bdaddr_t *addr; +	DBusMessage *msg; +	bonding_state_t bonding_state; +}; +  struct hci_dbus_data {  	uint16_t dev_id;  	uint16_t path_id; @@ -100,6 +111,7 @@ struct hci_dbus_data {  	struct slist *discovered_devices;  	char *requestor_name;	/* requestor unique name */  	struct slist *passkey_agents; +	struct slist *bonding_requests;  };  struct passkey_agent { diff --git a/hcid/hcid.h b/hcid/hcid.h index ca39bc1d..db2133c3 100644 --- a/hcid/hcid.h +++ b/hcid/hcid.h @@ -116,7 +116,7 @@ void hcid_dbus_inquiry_start(bdaddr_t *local);  void hcid_dbus_inquiry_complete(bdaddr_t *local);  void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, int8_t rssi);  void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name); -void hcid_dbus_conn_complete(bdaddr_t *local, bdaddr_t *peer); +void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, bdaddr_t *peer);  void hcid_dbus_disconn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t reason);  void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const uint8_t status);  void hcid_dbus_setname_complete(bdaddr_t *local); @@ -126,7 +126,7 @@ static inline void hcid_dbus_inquiry_start(bdaddr_t *local) {}  static inline void hcid_dbus_inquiry_complete(bdaddr_t *local) {}  static inline void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, int8_t rssi) {}  static inline void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name) {} -static inline void hcid_dbus_conn_complete(bdaddr_t *local, bdaddr_t *peer) {} +static inline void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, bdaddr_t *peer) {}  static inline void hcid_dbus_disconn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t reason) {}  static inline void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const uint8_t status) {}  static inline void hcid_dbus_setname_complete(bdaddr_t *local) {} @@ -174,33 +174,3 @@ void enable_debug();  void disable_debug();  void start_logging(const char *ident, const char *message);  void stop_logging(void); - -static inline int find_conn(int dd, int dev_id, long arg) -{ -	struct hci_conn_list_req *cl; -	struct hci_conn_info *ci; -	int i; - -	cl = malloc(10 * sizeof(*ci) + sizeof(*cl)); -	if (!cl) { -		error("Can't allocate memory"); -		return 0; -	} - -	cl->dev_id = dev_id; -	cl->conn_num = 10; -	ci = cl->conn_info; - -	if (ioctl(dd, HCIGETCONNLIST, (void *) cl)) { -		error("Can't get connection list"); -		return 0; -	} - -	for (i = 0; i < cl->conn_num; i++, ci++) -		if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) -			return 1; - -	free(cl); - -	return 0; -} diff --git a/hcid/security.c b/hcid/security.c index f3c03ef2..f42738a5 100644 --- a/hcid/security.c +++ b/hcid/security.c @@ -465,14 +465,14 @@ static inline void conn_complete(int dev, bdaddr_t *sba, void *ptr)  {  	evt_conn_complete *evt = ptr; +	hcid_dbus_conn_complete(sba, evt->status, evt->handle, &evt->bdaddr); +  	if (evt->status)  		return;  	update_lastused(sba, &evt->bdaddr);  	name_resolve(dev, &evt->bdaddr); - -	hcid_dbus_conn_complete(sba, &evt->bdaddr);  }  static inline void disconn_complete(int dev, bdaddr_t *sba, void *ptr) | 
