1/*-
2 * hcsecd.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: hcsecd.c,v 1.6 2003/08/18 19:19:55 max Exp $
31 */
32
33#include <sys/queue.h>
34#define L2CAP_SOCKET_CHECKED
35#include <bluetooth.h>
36#include <err.h>
37#include <errno.h>
38#include <signal.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <syslog.h>
44#include <unistd.h>
45#include "hcsecd.h"
46
47static int	done = 0;
48
49static int process_pin_code_request_event
50	(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr);
51static int process_link_key_request_event
52	(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr);
53static int send_pin_code_reply
54	(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, char const *pin);
55static int send_link_key_reply
56	(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, uint8_t *key);
57static int process_link_key_notification_event
58	(int sock, struct sockaddr_hci *addr, ng_hci_link_key_notification_ep *ep);
59static void sighup
60	(int s);
61static void sigint
62	(int s);
63static void usage
64	(void);
65
66/* Main */
67int
68main(int argc, char *argv[])
69{
70	int					 n, detach, sock;
71	socklen_t				 size;
72	struct sigaction			 sa;
73	struct sockaddr_hci			 addr;
74	struct ng_btsocket_hci_raw_filter	 filter;
75	char					 buffer[HCSECD_BUFFER_SIZE];
76	ng_hci_event_pkt_t			*event = NULL;
77
78	detach = 1;
79
80	while ((n = getopt(argc, argv, "df:h")) != -1) {
81		switch (n) {
82		case 'd':
83			detach = 0;
84			break;
85
86		case 'f':
87			config_file = optarg;
88			break;
89
90		case 'h':
91		default:
92			usage();
93			/* NOT REACHED */
94		}
95	}
96
97	if (config_file == NULL)
98		usage();
99		/* NOT REACHED */
100
101	if (getuid() != 0)
102		errx(1, "** ERROR: You should run %s as privileged user!",
103			HCSECD_IDENT);
104
105	/* Set signal handlers */
106	memset(&sa, 0, sizeof(sa));
107	sa.sa_handler = sigint;
108	sa.sa_flags = SA_NOCLDWAIT;
109	if (sigaction(SIGINT, &sa, NULL) < 0)
110		err(1, "Could not sigaction(SIGINT)");
111	if (sigaction(SIGTERM, &sa, NULL) < 0)
112		err(1, "Could not sigaction(SIGINT)");
113
114	memset(&sa, 0, sizeof(sa));
115	sa.sa_handler = sighup;
116	if (sigaction(SIGHUP, &sa, NULL) < 0)
117		err(1, "Could not sigaction(SIGHUP)");
118
119	/* Open socket and set filter */
120	sock = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
121	if (sock < 0)
122		err(1, "Could not create HCI socket");
123
124	memset(&filter, 0, sizeof(filter));
125	bit_set(filter.event_mask, NG_HCI_EVENT_PIN_CODE_REQ - 1);
126	bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_REQ - 1);
127	bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_NOTIFICATION - 1);
128
129	if (setsockopt(sock, SOL_HCI_RAW, SO_HCI_RAW_FILTER,
130			(void * const) &filter, sizeof(filter)) < 0)
131		err(1, "Could not set HCI socket filter");
132
133	if (detach && daemon(0, 0) < 0)
134		err(1, "Could not daemon()ize");
135
136	openlog(HCSECD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON);
137
138	read_config_file();
139	read_keys_file();
140
141	if (detach) {
142		FILE	*pid = NULL;
143
144		if ((pid = fopen(HCSECD_PIDFILE, "w")) == NULL) {
145			syslog(LOG_ERR, "Could not create PID file %s. %s (%d)",
146					HCSECD_PIDFILE, strerror(errno), errno);
147			exit(1);
148		}
149
150		fprintf(pid, "%d", getpid());
151		fclose(pid);
152	}
153
154	event = (ng_hci_event_pkt_t *) buffer;
155	while (!done) {
156		size = sizeof(addr);
157		n = recvfrom(sock, buffer, sizeof(buffer), 0,
158				(struct sockaddr *) &addr, &size);
159		if (n < 0) {
160			if (errno == EINTR)
161				continue;
162
163			syslog(LOG_ERR, "Could not receive from HCI socket. " \
164					"%s (%d)", strerror(errno), errno);
165			exit(1);
166		}
167
168		if (event->type != NG_HCI_EVENT_PKT) {
169			syslog(LOG_ERR, "Received unexpected HCI packet, " \
170					"type=%#x", event->type);
171			continue;
172		}
173
174		switch (event->event) {
175		case NG_HCI_EVENT_PIN_CODE_REQ:
176			process_pin_code_request_event(sock, &addr,
177							(bdaddr_p)(event + 1));
178			break;
179
180		case NG_HCI_EVENT_LINK_KEY_REQ:
181			process_link_key_request_event(sock, &addr,
182							(bdaddr_p)(event + 1));
183			break;
184
185		case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:
186			process_link_key_notification_event(sock, &addr,
187				(ng_hci_link_key_notification_ep *)(event + 1));
188			break;
189
190		default:
191			syslog(LOG_ERR, "Received unexpected HCI event, " \
192					"event=%#x", event->event);
193			break;
194		}
195	}
196
197	if (detach)
198		if (remove(HCSECD_PIDFILE) < 0)
199			syslog(LOG_ERR, "Could not remove PID file %s. %s (%d)",
200					HCSECD_PIDFILE, strerror(errno), errno);
201
202	dump_keys_file();
203	clean_config();
204	closelog();
205	close(sock);
206
207	return (0);
208}
209
210/* Process PIN_Code_Request event */
211static int
212process_pin_code_request_event(int sock, struct sockaddr_hci *addr,
213		bdaddr_p bdaddr)
214{
215	link_key_p	key = NULL;
216
217	syslog(LOG_DEBUG, "Got PIN_Code_Request event from '%s', " \
218			"remote bdaddr %s", addr->hci_node,
219			bt_ntoa(bdaddr, NULL));
220
221	if ((key = get_key(bdaddr, 0)) != NULL) {
222		syslog(LOG_DEBUG, "Found matching entry, " \
223				"remote bdaddr %s, name '%s', PIN code %s",
224				bt_ntoa(&key->bdaddr, NULL),
225				(key->name != NULL)? key->name : "No name",
226				(key->pin != NULL)? "exists" : "doesn't exist");
227
228		return (send_pin_code_reply(sock, addr, bdaddr, key->pin));
229	}
230
231	syslog(LOG_DEBUG, "Could not PIN code for remote bdaddr %s",
232			bt_ntoa(bdaddr, NULL));
233
234	return (send_pin_code_reply(sock, addr, bdaddr, NULL));
235}
236
237/* Process Link_Key_Request event */
238static int
239process_link_key_request_event(int sock, struct sockaddr_hci *addr,
240		bdaddr_p bdaddr)
241{
242	link_key_p	key = NULL;
243
244	syslog(LOG_DEBUG, "Got Link_Key_Request event from '%s', " \
245			"remote bdaddr %s", addr->hci_node,
246			bt_ntoa(bdaddr, NULL));
247
248	if ((key = get_key(bdaddr, 0)) != NULL) {
249		syslog(LOG_DEBUG, "Found matching entry, " \
250				"remote bdaddr %s, name '%s', link key %s",
251				bt_ntoa(&key->bdaddr, NULL),
252				(key->name != NULL)? key->name : "No name",
253				(key->key != NULL)? "exists" : "doesn't exist");
254
255		return (send_link_key_reply(sock, addr, bdaddr, key->key));
256	}
257
258	syslog(LOG_DEBUG, "Could not find link key for remote bdaddr %s",
259			bt_ntoa(bdaddr, NULL));
260
261	return (send_link_key_reply(sock, addr, bdaddr, NULL));
262}
263
264/* Send PIN_Code_[Negative]_Reply */
265static int
266send_pin_code_reply(int sock, struct sockaddr_hci *addr,
267		bdaddr_p bdaddr, char const *pin)
268{
269	uint8_t			 buffer[HCSECD_BUFFER_SIZE];
270	ng_hci_cmd_pkt_t	*cmd = NULL;
271
272	memset(buffer, 0, sizeof(buffer));
273
274	cmd = (ng_hci_cmd_pkt_t *) buffer;
275	cmd->type = NG_HCI_CMD_PKT;
276
277	if (pin != NULL) {
278		ng_hci_pin_code_rep_cp	*cp = NULL;
279
280		cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
281						NG_HCI_OCF_PIN_CODE_REP));
282		cmd->length = sizeof(*cp);
283
284		cp = (ng_hci_pin_code_rep_cp *)(cmd + 1);
285		memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
286		strncpy((char *) cp->pin, pin, sizeof(cp->pin));
287		cp->pin_size = strlen((char const *) cp->pin);
288
289		syslog(LOG_DEBUG, "Sending PIN_Code_Reply to '%s' " \
290				"for remote bdaddr %s",
291				addr->hci_node, bt_ntoa(bdaddr, NULL));
292	} else {
293		ng_hci_pin_code_neg_rep_cp	*cp = NULL;
294
295		cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
296						NG_HCI_OCF_PIN_CODE_NEG_REP));
297		cmd->length = sizeof(*cp);
298
299		cp = (ng_hci_pin_code_neg_rep_cp *)(cmd + 1);
300		memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
301
302		syslog(LOG_DEBUG, "Sending PIN_Code_Negative_Reply to '%s' " \
303				"for remote bdaddr %s",
304				addr->hci_node, bt_ntoa(bdaddr, NULL));
305	}
306
307again:
308	if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0,
309			(struct sockaddr *) addr, sizeof(*addr)) < 0) {
310		if (errno == EINTR)
311			goto again;
312
313		syslog(LOG_ERR, "Could not send PIN code reply to '%s' " \
314				"for remote bdaddr %s. %s (%d)",
315				addr->hci_node, bt_ntoa(bdaddr, NULL),
316				strerror(errno), errno);
317		return (-1);
318	}
319
320	return (0);
321}
322
323/* Send Link_Key_[Negative]_Reply */
324static int
325send_link_key_reply(int sock, struct sockaddr_hci *addr,
326		bdaddr_p bdaddr, uint8_t *key)
327{
328	uint8_t			 buffer[HCSECD_BUFFER_SIZE];
329	ng_hci_cmd_pkt_t	*cmd = NULL;
330
331	memset(buffer, 0, sizeof(buffer));
332
333	cmd = (ng_hci_cmd_pkt_t *) buffer;
334	cmd->type = NG_HCI_CMD_PKT;
335
336	if (key != NULL) {
337		ng_hci_link_key_rep_cp	*cp = NULL;
338
339		cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
340						NG_HCI_OCF_LINK_KEY_REP));
341		cmd->length = sizeof(*cp);
342
343		cp = (ng_hci_link_key_rep_cp *)(cmd + 1);
344		memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
345		memcpy(&cp->key, key, sizeof(cp->key));
346
347		syslog(LOG_DEBUG, "Sending Link_Key_Reply to '%s' " \
348				"for remote bdaddr %s",
349				addr->hci_node, bt_ntoa(bdaddr, NULL));
350	} else {
351		ng_hci_link_key_neg_rep_cp	*cp = NULL;
352
353		cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
354						NG_HCI_OCF_LINK_KEY_NEG_REP));
355		cmd->length = sizeof(*cp);
356
357		cp = (ng_hci_link_key_neg_rep_cp *)(cmd + 1);
358		memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
359
360		syslog(LOG_DEBUG, "Sending Link_Key_Negative_Reply to '%s' " \
361				"for remote bdaddr %s",
362				addr->hci_node, bt_ntoa(bdaddr, NULL));
363	}
364
365again:
366	if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0,
367			(struct sockaddr *) addr, sizeof(*addr)) < 0) {
368		if (errno == EINTR)
369			goto again;
370
371		syslog(LOG_ERR, "Could not send link key reply to '%s' " \
372				"for remote bdaddr %s. %s (%d)",
373				addr->hci_node, bt_ntoa(bdaddr, NULL),
374				strerror(errno), errno);
375		return (-1);
376	}
377
378	return (0);
379}
380
381/* Process Link_Key_Notification event */
382static int
383process_link_key_notification_event(int sock, struct sockaddr_hci *addr,
384		ng_hci_link_key_notification_ep *ep)
385{
386	link_key_p	key = NULL;
387
388	syslog(LOG_DEBUG, "Got Link_Key_Notification event from '%s', " \
389			"remote bdaddr %s", addr->hci_node,
390			bt_ntoa(&ep->bdaddr, NULL));
391
392	if ((key = get_key(&ep->bdaddr, 1)) == NULL) {
393		syslog(LOG_ERR, "Could not find entry for remote bdaddr %s",
394				bt_ntoa(&ep->bdaddr, NULL));
395		return (-1);
396	}
397
398	syslog(LOG_DEBUG, "Updating link key for the entry, " \
399			"remote bdaddr %s, name '%s', link key %s",
400			bt_ntoa(&key->bdaddr, NULL),
401			(key->name != NULL)? key->name : "No name",
402			(key->key != NULL)? "exists" : "doesn't exist");
403
404	if (key->key == NULL) {
405		key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE);
406		if (key->key == NULL) {
407			syslog(LOG_ERR, "Could not allocate link key");
408			exit(1);
409		}
410	}
411
412	memcpy(key->key, &ep->key, NG_HCI_KEY_SIZE);
413
414	return (0);
415}
416
417/* Signal handlers */
418static void
419sighup(int s)
420{
421	syslog(LOG_DEBUG, "Got SIGHUP (%d)", s);
422
423	dump_keys_file();
424	read_config_file();
425	read_keys_file();
426}
427
428static void
429sigint(int s)
430{
431	syslog(LOG_DEBUG, "Got signal %d, total number of signals %d",
432			s, ++ done);
433}
434
435/* Display usage and exit */
436static void
437usage(void)
438{
439	fprintf(stderr,
440"Usage: %s [-d] -f config_file [-h]\n" \
441"Where:\n" \
442"\t-d              do not detach from terminal\n" \
443"\t-f config_file  use <config_file>\n" \
444"\t-h              display this message\n", HCSECD_IDENT);
445
446	exit(255);
447}
448
449