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