xref: /netbsd-src/usr.sbin/bthcid/hci.c (revision bc3dcc186ac2e2af0bea9e4629caa6c3c833561e)
1*bc3dcc18Smlelstv /*	$NetBSD: hci.c,v 1.4 2024/09/07 13:57:25 mlelstv Exp $	*/
2a5c89047Sgdamore 
3a5c89047Sgdamore /*-
4a5c89047Sgdamore  * Copyright (c) 2006 Itronix Inc.
5a5c89047Sgdamore  * All rights reserved.
6a5c89047Sgdamore  *
7a5c89047Sgdamore  * Redistribution and use in source and binary forms, with or without
8a5c89047Sgdamore  * modification, are permitted provided that the following conditions
9a5c89047Sgdamore  * are met:
10a5c89047Sgdamore  * 1. Redistributions of source code must retain the above copyright
11a5c89047Sgdamore  *    notice, this list of conditions and the following disclaimer.
12a5c89047Sgdamore  * 2. Redistributions in binary form must reproduce the above copyright
13a5c89047Sgdamore  *    notice, this list of conditions and the following disclaimer in the
14a5c89047Sgdamore  *    documentation and/or other materials provided with the distribution.
15a5c89047Sgdamore  * 3. The name of Itronix Inc. may not be used to endorse
16a5c89047Sgdamore  *    or promote products derived from this software without specific
17a5c89047Sgdamore  *    prior written permission.
18a5c89047Sgdamore  *
19a5c89047Sgdamore  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20a5c89047Sgdamore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21a5c89047Sgdamore  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22a5c89047Sgdamore  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23a5c89047Sgdamore  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24a5c89047Sgdamore  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25a5c89047Sgdamore  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26a5c89047Sgdamore  * ON ANY THEORY OF LIABILITY, WHETHER IN
27a5c89047Sgdamore  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28a5c89047Sgdamore  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29a5c89047Sgdamore  * POSSIBILITY OF SUCH DAMAGE.
30a5c89047Sgdamore  */
31a5c89047Sgdamore /*
32a5c89047Sgdamore  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
33a5c89047Sgdamore  * All rights reserved.
34a5c89047Sgdamore  *
35a5c89047Sgdamore  * Redistribution and use in source and binary forms, with or without
36a5c89047Sgdamore  * modification, are permitted provided that the following conditions
37a5c89047Sgdamore  * are met:
38a5c89047Sgdamore  * 1. Redistributions of source code must retain the above copyright
39a5c89047Sgdamore  *    notice, this list of conditions and the following disclaimer.
40a5c89047Sgdamore  * 2. Redistributions in binary form must reproduce the above copyright
41a5c89047Sgdamore  *    notice, this list of conditions and the following disclaimer in the
42a5c89047Sgdamore  *    documentation and/or other materials provided with the distribution.
43a5c89047Sgdamore  *
44a5c89047Sgdamore  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45a5c89047Sgdamore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46a5c89047Sgdamore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47a5c89047Sgdamore  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48a5c89047Sgdamore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49a5c89047Sgdamore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50a5c89047Sgdamore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51a5c89047Sgdamore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52a5c89047Sgdamore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53a5c89047Sgdamore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54a5c89047Sgdamore  * SUCH DAMAGE.
55a5c89047Sgdamore  */
56a5c89047Sgdamore 
57a5c89047Sgdamore #include <sys/cdefs.h>
58*bc3dcc18Smlelstv __RCSID("$NetBSD: hci.c,v 1.4 2024/09/07 13:57:25 mlelstv Exp $");
59a5c89047Sgdamore 
60a5c89047Sgdamore #include <sys/ioctl.h>
61a5c89047Sgdamore #include <sys/time.h>
62a5c89047Sgdamore #include <bluetooth.h>
63a5c89047Sgdamore #include <errno.h>
64a5c89047Sgdamore #include <event.h>
65a5c89047Sgdamore #include <string.h>
66a5c89047Sgdamore #include <syslog.h>
67a5c89047Sgdamore #include <unistd.h>
68a5c89047Sgdamore 
69a5c89047Sgdamore #include "bthcid.h"
70a5c89047Sgdamore 
71a5c89047Sgdamore static struct event	hci_ev;
72a5c89047Sgdamore 
73a5c89047Sgdamore static void process_hci
74a5c89047Sgdamore 		(int, short, void *);
75a5c89047Sgdamore 
76a5c89047Sgdamore static int process_pin_code_request_event
77a5c89047Sgdamore 		(int, struct sockaddr_bt *, bdaddr_t *);
78a5c89047Sgdamore static int process_link_key_request_event
79a5c89047Sgdamore 		(int, struct sockaddr_bt *, bdaddr_t *);
80a5c89047Sgdamore static int process_link_key_notification_event
81a5c89047Sgdamore 		(int, struct sockaddr_bt *, hci_link_key_notification_ep *);
82a5c89047Sgdamore 
83a5c89047Sgdamore static int send_link_key_reply
84a5c89047Sgdamore 		(int, struct sockaddr_bt *, bdaddr_t *, uint8_t *);
85a5c89047Sgdamore static int send_hci_cmd
86a5c89047Sgdamore 		(int, struct sockaddr_bt *, uint16_t, size_t, void *);
87a5c89047Sgdamore 
88a5c89047Sgdamore static char dev_name[HCI_DEVNAME_SIZE];
89a5c89047Sgdamore 
90a5c89047Sgdamore /* Initialise HCI Events */
91a5c89047Sgdamore int
9289a4552cSplunky init_hci(const char *device)
93a5c89047Sgdamore {
9489a4552cSplunky 	struct bt_devfilter	filter;
95a5c89047Sgdamore 	int			hci;
96a5c89047Sgdamore 
9789a4552cSplunky 	hci = bt_devopen(device, 0);
98a5c89047Sgdamore 	if (hci < 0)
99a5c89047Sgdamore 		return -1;
100a5c89047Sgdamore 
101a5c89047Sgdamore 	memset(&filter, 0, sizeof(filter));
10289a4552cSplunky 	bt_devfilter_pkt_set(&filter, HCI_EVENT_PKT);
10389a4552cSplunky 	bt_devfilter_evt_set(&filter, HCI_EVENT_PIN_CODE_REQ);
10489a4552cSplunky 	bt_devfilter_evt_set(&filter, HCI_EVENT_LINK_KEY_REQ);
10589a4552cSplunky 	bt_devfilter_evt_set(&filter, HCI_EVENT_LINK_KEY_NOTIFICATION);
10689a4552cSplunky 	if (bt_devfilter(hci, &filter, NULL) < 0) {
107a5c89047Sgdamore 		close(hci);
108a5c89047Sgdamore 		return -1;
109a5c89047Sgdamore 	}
110a5c89047Sgdamore 
111a5c89047Sgdamore 	event_set(&hci_ev, hci, EV_READ | EV_PERSIST, process_hci, NULL);
112a5c89047Sgdamore 	if (event_add(&hci_ev, NULL) < 0) {
113a5c89047Sgdamore 		close(hci);
114a5c89047Sgdamore 		return -1;
115a5c89047Sgdamore 	}
116a5c89047Sgdamore 
117a5c89047Sgdamore 	return 0;
118a5c89047Sgdamore }
119a5c89047Sgdamore 
120a5c89047Sgdamore /* Process an HCI event */
121a5c89047Sgdamore static void
122a5c89047Sgdamore process_hci(int sock, short ev, void *arg)
123a5c89047Sgdamore {
124a5c89047Sgdamore 	char			 buffer[HCI_EVENT_PKT_SIZE];
125a5c89047Sgdamore 	hci_event_hdr_t		*event = (hci_event_hdr_t *)buffer;
126a5c89047Sgdamore 	struct sockaddr_bt	 addr;
127a5c89047Sgdamore 	int			 n;
128a5c89047Sgdamore 	socklen_t		 size;
129a5c89047Sgdamore 
130a5c89047Sgdamore 	size = sizeof(addr);
131a5c89047Sgdamore 	n = recvfrom(sock, buffer, sizeof(buffer), 0,
132a5c89047Sgdamore 			(struct sockaddr *) &addr, &size);
133a5c89047Sgdamore 	if (n < 0) {
1344c6d0d2aSplunky 		syslog(LOG_ERR, "Could not receive from HCI socket: %m");
135a5c89047Sgdamore 		return;
136a5c89047Sgdamore 	}
137a5c89047Sgdamore 
138a5c89047Sgdamore 	if (event->type != HCI_EVENT_PKT) {
139a5c89047Sgdamore 		syslog(LOG_ERR, "Received unexpected HCI packet, "
140a5c89047Sgdamore 				"type=%#x", event->type);
141a5c89047Sgdamore 
142a5c89047Sgdamore 		return;
143a5c89047Sgdamore 	}
144a5c89047Sgdamore 
145a5c89047Sgdamore 	if (!bt_devname(dev_name, &addr.bt_bdaddr))
146a5c89047Sgdamore 		strlcpy(dev_name, "unknown", sizeof(dev_name));
147a5c89047Sgdamore 
148a5c89047Sgdamore 	switch (event->event) {
149a5c89047Sgdamore 	case HCI_EVENT_PIN_CODE_REQ:
150a5c89047Sgdamore 		process_pin_code_request_event(sock, &addr,
151a5c89047Sgdamore 					    (bdaddr_t *)(event + 1));
152a5c89047Sgdamore 		break;
153a5c89047Sgdamore 
154a5c89047Sgdamore 	case HCI_EVENT_LINK_KEY_REQ:
155a5c89047Sgdamore 		process_link_key_request_event(sock, &addr,
156a5c89047Sgdamore 					    (bdaddr_t *)(event + 1));
157a5c89047Sgdamore 		break;
158a5c89047Sgdamore 
159a5c89047Sgdamore 	case HCI_EVENT_LINK_KEY_NOTIFICATION:
160a5c89047Sgdamore 		process_link_key_notification_event(sock, &addr,
161a5c89047Sgdamore 			(hci_link_key_notification_ep *)(event + 1));
162a5c89047Sgdamore 		break;
163a5c89047Sgdamore 
164a5c89047Sgdamore 	default:
165a5c89047Sgdamore 		syslog(LOG_ERR, "Received unexpected HCI event, "
166a5c89047Sgdamore 				"event=%#x", event->event);
167a5c89047Sgdamore 		break;
168a5c89047Sgdamore 	}
169a5c89047Sgdamore 
170a5c89047Sgdamore 	return;
171a5c89047Sgdamore }
172a5c89047Sgdamore 
173a5c89047Sgdamore /* Process PIN_Code_Request event */
174a5c89047Sgdamore static int
175a5c89047Sgdamore process_pin_code_request_event(int sock, struct sockaddr_bt *addr,
176a5c89047Sgdamore 		bdaddr_t *bdaddr)
177a5c89047Sgdamore {
178a5c89047Sgdamore 	uint8_t	*pin;
179a5c89047Sgdamore 
180a5c89047Sgdamore 	syslog(LOG_DEBUG, "Got PIN_Code_Request event from %s, "
181a5c89047Sgdamore 			  "remote bdaddr %s",
182a5c89047Sgdamore 			  dev_name,
183a5c89047Sgdamore 			  bt_ntoa(bdaddr, NULL));
184a5c89047Sgdamore 
185a5c89047Sgdamore 	pin = lookup_pin(&addr->bt_bdaddr, bdaddr);
186a5c89047Sgdamore 	if (pin != NULL)
187a5c89047Sgdamore 		return send_pin_code_reply(sock, addr, bdaddr, pin);
188a5c89047Sgdamore 
189a5c89047Sgdamore 	if (send_client_request(&addr->bt_bdaddr, bdaddr, sock) == 0)
190a5c89047Sgdamore 		return send_pin_code_reply(sock, addr, bdaddr, NULL);
191a5c89047Sgdamore 
192a5c89047Sgdamore 	return 0;
193a5c89047Sgdamore }
194a5c89047Sgdamore 
195a5c89047Sgdamore /* Process Link_Key_Request event */
196a5c89047Sgdamore static int
197a5c89047Sgdamore process_link_key_request_event(int sock, struct sockaddr_bt *addr,
198a5c89047Sgdamore 		bdaddr_t *bdaddr)
199a5c89047Sgdamore {
200a5c89047Sgdamore 	uint8_t		*key;
201a5c89047Sgdamore 
202a5c89047Sgdamore 	syslog(LOG_DEBUG,
203a5c89047Sgdamore 		"Got Link_Key_Request event from %s, remote bdaddr %s",
204a5c89047Sgdamore 		dev_name, bt_ntoa(bdaddr, NULL));
205a5c89047Sgdamore 
206a5c89047Sgdamore 	key = lookup_key(&addr->bt_bdaddr, bdaddr);
207a5c89047Sgdamore 
208a5c89047Sgdamore 	if (key != NULL) {
209a5c89047Sgdamore 		syslog(LOG_DEBUG, "Found Key, remote bdaddr %s",
210a5c89047Sgdamore 				bt_ntoa(bdaddr, NULL));
211a5c89047Sgdamore 
212a5c89047Sgdamore 		return send_link_key_reply(sock, addr, bdaddr, key);
213a5c89047Sgdamore 	}
214a5c89047Sgdamore 
215a5c89047Sgdamore 	syslog(LOG_DEBUG, "Could not find link key for remote bdaddr %s",
216a5c89047Sgdamore 			bt_ntoa(bdaddr, NULL));
217a5c89047Sgdamore 
218a5c89047Sgdamore 	return send_link_key_reply(sock, addr, bdaddr, NULL);
219a5c89047Sgdamore }
220a5c89047Sgdamore 
221a5c89047Sgdamore /* Send PIN_Code_[Negative]_Reply */
222a5c89047Sgdamore int
223a5c89047Sgdamore send_pin_code_reply(int sock, struct sockaddr_bt *addr,
224a5c89047Sgdamore 	bdaddr_t *bdaddr, uint8_t *pin)
225a5c89047Sgdamore {
226a5c89047Sgdamore 	int	n;
227a5c89047Sgdamore 
228a5c89047Sgdamore 	if (pin != NULL) {
229a5c89047Sgdamore 		hci_pin_code_rep_cp	 cp;
230a5c89047Sgdamore 
231a5c89047Sgdamore 		syslog(LOG_DEBUG, "Sending PIN_Code_Reply to %s "
232a5c89047Sgdamore 				  "for remote bdaddr %s",
233a5c89047Sgdamore 				  dev_name,
234a5c89047Sgdamore 				  bt_ntoa(bdaddr, NULL));
235a5c89047Sgdamore 
236a5c89047Sgdamore 		bdaddr_copy(&cp.bdaddr, bdaddr);
237a5c89047Sgdamore 		memcpy(cp.pin, pin, HCI_PIN_SIZE);
238a5c89047Sgdamore 
239a5c89047Sgdamore 		n = HCI_PIN_SIZE;
240a5c89047Sgdamore 		while (n > 0 && pin[n - 1] == 0)
241a5c89047Sgdamore 			n--;
242a5c89047Sgdamore 		cp.pin_size = n;
243a5c89047Sgdamore 
244a5c89047Sgdamore 		n = send_hci_cmd(sock, addr,
245a5c89047Sgdamore 				HCI_CMD_PIN_CODE_REP, sizeof(cp), &cp);
246a5c89047Sgdamore 
247a5c89047Sgdamore 	} else {
248a5c89047Sgdamore 		syslog(LOG_DEBUG, "Sending PIN_Code_Negative_Reply to %s "
249a5c89047Sgdamore 				  "for remote bdaddr %s",
250a5c89047Sgdamore 				  dev_name,
251a5c89047Sgdamore 				  bt_ntoa(bdaddr, NULL));
252a5c89047Sgdamore 
253a5c89047Sgdamore 		n = send_hci_cmd(sock, addr, HCI_CMD_PIN_CODE_NEG_REP,
254a5c89047Sgdamore 					sizeof(bdaddr_t), bdaddr);
255a5c89047Sgdamore 	}
256a5c89047Sgdamore 
257a5c89047Sgdamore 	if (n < 0) {
258a5c89047Sgdamore 		syslog(LOG_ERR, "Could not send PIN code reply to %s "
2594c6d0d2aSplunky 				"for remote bdaddr %s: %m",
260a5c89047Sgdamore 				dev_name,
2614c6d0d2aSplunky 				bt_ntoa(bdaddr, NULL));
262a5c89047Sgdamore 
263a5c89047Sgdamore 		return -1;
264a5c89047Sgdamore 	}
265a5c89047Sgdamore 
266a5c89047Sgdamore 	return 0;
267a5c89047Sgdamore }
268a5c89047Sgdamore 
269a5c89047Sgdamore /* Send Link_Key_[Negative]_Reply */
270a5c89047Sgdamore static int
271a5c89047Sgdamore send_link_key_reply(int sock, struct sockaddr_bt *addr,
272a5c89047Sgdamore 		bdaddr_t *bdaddr, uint8_t *key)
273a5c89047Sgdamore {
274a5c89047Sgdamore 	int	n;
275a5c89047Sgdamore 
276a5c89047Sgdamore 	if (key != NULL) {
277a5c89047Sgdamore 		hci_link_key_rep_cp	cp;
278a5c89047Sgdamore 
279a5c89047Sgdamore 		bdaddr_copy(&cp.bdaddr, bdaddr);
280a5c89047Sgdamore 		memcpy(&cp.key, key, sizeof(cp.key));
281a5c89047Sgdamore 
282a5c89047Sgdamore 		syslog(LOG_DEBUG, "Sending Link_Key_Reply to %s "
283a5c89047Sgdamore 				"for remote bdaddr %s",
284a5c89047Sgdamore 				dev_name, bt_ntoa(bdaddr, NULL));
285a5c89047Sgdamore 
286a5c89047Sgdamore 		n = send_hci_cmd(sock, addr, HCI_CMD_LINK_KEY_REP, sizeof(cp), &cp);
287a5c89047Sgdamore 	} else {
288a5c89047Sgdamore 		hci_link_key_neg_rep_cp	cp;
289a5c89047Sgdamore 
290a5c89047Sgdamore 		bdaddr_copy(&cp.bdaddr, bdaddr);
291a5c89047Sgdamore 
292a5c89047Sgdamore 		syslog(LOG_DEBUG, "Sending Link_Key_Negative_Reply to %s "
293a5c89047Sgdamore 				"for remote bdaddr %s",
294a5c89047Sgdamore 				dev_name, bt_ntoa(bdaddr, NULL));
295a5c89047Sgdamore 
296a5c89047Sgdamore 		n = send_hci_cmd(sock, addr, HCI_CMD_LINK_KEY_NEG_REP, sizeof(cp), &cp);
297a5c89047Sgdamore 	}
298a5c89047Sgdamore 
299a5c89047Sgdamore 	if (n < 0) {
300a5c89047Sgdamore 		syslog(LOG_ERR, "Could not send link key reply to %s "
3014c6d0d2aSplunky 				"for remote bdaddr %s: %m",
3024c6d0d2aSplunky 				dev_name, bt_ntoa(bdaddr, NULL));
303a5c89047Sgdamore 		return -1;
304a5c89047Sgdamore 	}
305a5c89047Sgdamore 
306a5c89047Sgdamore 	return 0;
307a5c89047Sgdamore }
308a5c89047Sgdamore 
309a5c89047Sgdamore /* Process Link_Key_Notification event */
310a5c89047Sgdamore static int
311a5c89047Sgdamore process_link_key_notification_event(int sock, struct sockaddr_bt *addr,
312a5c89047Sgdamore 		hci_link_key_notification_ep *ep)
313a5c89047Sgdamore {
314a5c89047Sgdamore 
315a5c89047Sgdamore 	syslog(LOG_DEBUG, "Got Link_Key_Notification event from %s, "
316a5c89047Sgdamore 			"remote bdaddr %s",
317a5c89047Sgdamore 			dev_name,
318a5c89047Sgdamore 			bt_ntoa(&ep->bdaddr, NULL));
319a5c89047Sgdamore 
320a5c89047Sgdamore 	save_key(&addr->bt_bdaddr, &ep->bdaddr, ep->key);
321a5c89047Sgdamore 	return 0;
322a5c89047Sgdamore }
323a5c89047Sgdamore 
324a5c89047Sgdamore /* Send HCI Command Packet to socket */
325a5c89047Sgdamore static int
326a5c89047Sgdamore send_hci_cmd(int sock, struct sockaddr_bt *sa, uint16_t opcode, size_t len, void *buf)
327a5c89047Sgdamore {
328a5c89047Sgdamore 	char msg[HCI_CMD_PKT_SIZE];
329a5c89047Sgdamore 	hci_cmd_hdr_t *h = (hci_cmd_hdr_t *)msg;
330a5c89047Sgdamore 
331a5c89047Sgdamore 	h->type = HCI_CMD_PKT;
332a5c89047Sgdamore 	h->opcode = htole16(opcode);
333a5c89047Sgdamore 	h->length = len;
334a5c89047Sgdamore 
335a5c89047Sgdamore 	if (len > 0)
336a5c89047Sgdamore 		memcpy(msg + sizeof(hci_cmd_hdr_t), buf, len);
337a5c89047Sgdamore 
338*bc3dcc18Smlelstv 	return sendto(sock, msg, sizeof(hci_cmd_hdr_t) + len, MSG_NOSIGNAL,
339a5c89047Sgdamore 			(struct sockaddr *)sa, sizeof(*sa));
340a5c89047Sgdamore }
341