1%{
2/*
3 * parser.y
4 *
5 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Id: parser.y,v 1.5 2003/06/07 21:22:30 max Exp $
30 * $FreeBSD$
31 */
32
33#include <sys/fcntl.h>
34#include <sys/queue.h>
35#include <bluetooth.h>
36#include <errno.h>
37#include <limits.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <stdarg.h>
41#include <string.h>
42#include <syslog.h>
43#include <unistd.h>
44#include "hcsecd.h"
45
46	int	yyparse  (void);
47	int	yylex    (void);
48
49static	void	free_key (link_key_p key);
50static	int	hexa2int4(char *a);
51static	int	hexa2int8(char *a);
52
53extern	int			 yylineno;
54static	LIST_HEAD(, link_key)	 link_keys;
55	char			*config_file = "/etc/bluetooth/hcsecd.conf";
56
57static	link_key_p		 key = NULL;
58%}
59
60%union {
61	char	*string;
62}
63
64%token <string> T_BDADDRSTRING T_HEXSTRING T_STRING
65%token T_DEVICE T_BDADDR T_NAME T_KEY T_PIN T_NOKEY T_NOPIN T_JUNK
66
67%%
68
69config:		line
70		| config line
71		;
72
73line:		T_DEVICE
74			{
75			key = (link_key_p) malloc(sizeof(*key));
76			if (key == NULL) {
77				syslog(LOG_ERR, "Could not allocate new " \
78						"config entry");
79				exit(1);
80			}
81
82			memset(key, 0, sizeof(*key));
83			}
84		'{' options '}'
85			{
86			if (get_key(&key->bdaddr, 1) != NULL) {
87				syslog(LOG_ERR, "Ignoring duplicated entry " \
88						"for bdaddr %s",
89						bt_ntoa(&key->bdaddr, NULL));
90				free_key(key);
91			} else
92				LIST_INSERT_HEAD(&link_keys, key, next);
93
94			key = NULL;
95			}
96		;
97
98options:	option ';'
99		| options option ';'
100		;
101
102option:		bdaddr
103		| name
104		| key
105		| pin
106		;
107
108bdaddr:		T_BDADDR T_BDADDRSTRING
109			{
110			if (!bt_aton($2, &key->bdaddr)) {
111				syslog(LOG_ERR, "Cound not parse BD_ADDR " \
112						"'%s'", $2);
113				exit(1);
114			}
115			}
116		;
117
118name:		T_NAME T_STRING
119			{
120			if (key->name != NULL)
121				free(key->name);
122
123			key->name = strdup($2);
124			if (key->name == NULL) {
125				syslog(LOG_ERR, "Could not allocate new " \
126						"device name");
127				exit(1);
128			}
129			}
130		;
131
132key:		T_KEY T_HEXSTRING
133			{
134			int	i, len;
135
136			if (key->key != NULL)
137				free(key->key);
138
139			key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE);
140			if (key->key == NULL) {
141				syslog(LOG_ERR, "Could not allocate new " \
142						"link key");
143				exit(1);
144			}
145
146			memset(key->key, 0, NG_HCI_KEY_SIZE);
147
148			len = strlen($2) / 2;
149			if (len > NG_HCI_KEY_SIZE)
150				len = NG_HCI_KEY_SIZE;
151
152			for (i = 0; i < len; i ++)
153				key->key[i] = hexa2int8((char *)($2) + 2*i);
154			}
155		| T_KEY T_NOKEY
156			{
157			if (key->key != NULL)
158				free(key->key);
159
160			key->key = NULL;
161			}
162		;
163
164pin:		T_PIN T_STRING
165			{
166			if (key->pin != NULL)
167				free(key->pin);
168
169			key->pin = strdup($2);
170			if (key->pin == NULL) {
171				syslog(LOG_ERR, "Could not allocate new " \
172						"PIN code");
173				exit(1);
174			}
175			}
176		| T_PIN T_NOPIN
177			{
178			if (key->pin != NULL)
179				free(key->pin);
180
181			key->pin = NULL;
182			}
183		;
184
185%%
186
187/* Display parser error message */
188void
189yyerror(char const *message)
190{
191	syslog(LOG_ERR, "%s in line %d", message, yylineno);
192}
193
194/* Re-read config file */
195void
196read_config_file(void)
197{
198	extern FILE	*yyin;
199
200	if (config_file == NULL) {
201		syslog(LOG_ERR, "Unknown config file name!");
202		exit(1);
203	}
204
205	if ((yyin = fopen(config_file, "r")) == NULL) {
206		syslog(LOG_ERR, "Could not open config file '%s'. %s (%d)",
207				config_file, strerror(errno), errno);
208		exit(1);
209	}
210
211	clean_config();
212	if (yyparse() < 0) {
213		syslog(LOG_ERR, "Could not parse config file '%s'",config_file);
214		exit(1);
215	}
216
217	fclose(yyin);
218	yyin = NULL;
219
220#if __config_debug__
221	dump_config();
222#endif
223}
224
225/* Clean config */
226void
227clean_config(void)
228{
229	link_key_p	key = NULL;
230
231	while ((key = LIST_FIRST(&link_keys)) != NULL) {
232		LIST_REMOVE(key, next);
233		free_key(key);
234	}
235}
236
237/* Find link key entry in the list. Return exact or default match */
238link_key_p
239get_key(bdaddr_p bdaddr, int exact_match)
240{
241	link_key_p	key = NULL, defkey = NULL;
242
243	LIST_FOREACH(key, &link_keys, next) {
244		if (memcmp(bdaddr, &key->bdaddr, sizeof(key->bdaddr)) == 0)
245			break;
246
247		if (!exact_match)
248			if (memcmp(NG_HCI_BDADDR_ANY, &key->bdaddr,
249					sizeof(key->bdaddr)) == 0)
250				defkey = key;
251	}
252
253	return ((key != NULL)? key : defkey);
254}
255
256#if __config_debug__
257/* Dump config */
258void
259dump_config(void)
260{
261	link_key_p	key = NULL;
262	char		buffer[64];
263
264	LIST_FOREACH(key, &link_keys, next) {
265		if (key->key != NULL)
266			snprintf(buffer, sizeof(buffer),
267"0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
268				key->key[0], key->key[1], key->key[2],
269				key->key[3], key->key[4], key->key[5],
270				key->key[6], key->key[7], key->key[8],
271				key->key[9], key->key[10], key->key[11],
272				key->key[12], key->key[13], key->key[14],
273				key->key[15]);
274
275		syslog(LOG_DEBUG,
276"device %s " \
277"bdaddr %s " \
278"pin %s " \
279"key %s",
280			(key->name != NULL)? key->name : "noname",
281			bt_ntoa(&key->bdaddr, NULL),
282			(key->pin != NULL)? key->pin : "nopin",
283			(key->key != NULL)? buffer : "nokey");
284	}
285}
286#endif
287
288/* Read keys file */
289int
290read_keys_file(void)
291{
292	FILE		*f = NULL;
293	link_key_t	*key = NULL;
294	char		 buf[HCSECD_BUFFER_SIZE], *p = NULL, *cp = NULL;
295	bdaddr_t	 bdaddr;
296	int		 i, len;
297
298	if ((f = fopen(HCSECD_KEYSFILE, "r")) == NULL) {
299		if (errno == ENOENT)
300			return (0);
301
302		syslog(LOG_ERR, "Could not open keys file %s. %s (%d)\n",
303				HCSECD_KEYSFILE, strerror(errno), errno);
304
305		return (-1);
306	}
307
308	while ((p = fgets(buf, sizeof(buf), f)) != NULL) {
309		if (*p == '#')
310			continue;
311		if ((cp = strpbrk(p, " ")) == NULL)
312			continue;
313
314		*cp++ = '\0';
315
316		if (!bt_aton(p, &bdaddr))
317			continue;
318
319		if ((key = get_key(&bdaddr, 1)) == NULL)
320			continue;
321
322		if (key->key == NULL) {
323			key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE);
324			if (key->key == NULL) {
325				syslog(LOG_ERR, "Could not allocate link key");
326				exit(1);
327			}
328		}
329
330		memset(key->key, 0, NG_HCI_KEY_SIZE);
331
332		len = strlen(cp) / 2;
333		if (len > NG_HCI_KEY_SIZE)
334			len = NG_HCI_KEY_SIZE;
335
336		for (i = 0; i < len; i ++)
337			key->key[i] = hexa2int8(cp + 2*i);
338
339		syslog(LOG_DEBUG, "Restored link key for the entry, " \
340				"remote bdaddr %s, name '%s'",
341				bt_ntoa(&key->bdaddr, NULL),
342				(key->name != NULL)? key->name : "No name");
343	}
344
345	fclose(f);
346
347	return (0);
348}
349
350/* Dump keys file */
351int
352dump_keys_file(void)
353{
354	link_key_p	key = NULL;
355	char		tmp[PATH_MAX], buf[HCSECD_BUFFER_SIZE];
356	int		f;
357
358	snprintf(tmp, sizeof(tmp), "%s.tmp", HCSECD_KEYSFILE);
359	if ((f = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) < 0) {
360		syslog(LOG_ERR, "Could not create temp keys file %s. %s (%d)\n",
361				tmp, strerror(errno), errno);
362		return (-1);
363	}
364
365	LIST_FOREACH(key, &link_keys, next) {
366		if (key->key == NULL)
367			continue;
368
369		snprintf(buf, sizeof(buf),
370"%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
371			bt_ntoa(&key->bdaddr, NULL),
372			key->key[0],  key->key[1],  key->key[2],  key->key[3],
373			key->key[4],  key->key[5],  key->key[6],  key->key[7],
374			key->key[8],  key->key[9],  key->key[10], key->key[11],
375			key->key[12], key->key[13], key->key[14], key->key[15]);
376
377		if (write(f, buf, strlen(buf)) < 0) {
378			syslog(LOG_ERR, "Could not write temp keys file. " \
379					"%s (%d)\n", strerror(errno), errno);
380			break;
381		}
382	}
383
384	close(f);
385
386	if (rename(tmp, HCSECD_KEYSFILE) < 0) {
387		syslog(LOG_ERR, "Could not rename(%s, %s). %s (%d)\n",
388				tmp, HCSECD_KEYSFILE, strerror(errno), errno);
389		unlink(tmp);
390		return (-1);
391	}
392
393	return (0);
394}
395
396/* Free key entry */
397static void
398free_key(link_key_p key)
399{
400	if (key->name != NULL)
401		free(key->name);
402	if (key->key != NULL)
403		free(key->key);
404	if (key->pin != NULL)
405		free(key->pin);
406
407	memset(key, 0, sizeof(*key));
408	free(key);
409}
410
411/* Convert hex ASCII to int4 */
412static int
413hexa2int4(char *a)
414{
415	if ('0' <= *a && *a <= '9')
416		return (*a - '0');
417
418	if ('A' <= *a && *a <= 'F')
419		return (*a - 'A' + 0xa);
420
421	if ('a' <= *a && *a <= 'f')
422		return (*a - 'a' + 0xa);
423
424	syslog(LOG_ERR, "Invalid hex character: '%c' (%#x)", *a, *a);
425	exit(1);
426}
427
428/* Convert hex ASCII to int8 */
429static int
430hexa2int8(char *a)
431{
432	return ((hexa2int4(a) << 4) | hexa2int4(a + 1));
433}
434
435