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