]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidd/parser.y
kldxref: Avoid buffer overflows in parse_pnp_list
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / bthidd / parser.y
1 %{
2 /*
3  * parser.y
4  */
5
6 /*-
7  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
8  *
9  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
34  * $FreeBSD$
35  */
36
37 #include <sys/queue.h>
38 #define L2CAP_SOCKET_CHECKED
39 #include <bluetooth.h>
40 #include <dev/usb/usb.h>
41 #include <dev/usb/usbhid.h>
42 #include <errno.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <usbhid.h>
49
50 #ifndef BTHIDCONTROL
51 #include <stdarg.h>
52 #include <syslog.h>
53 #define SYSLOG          syslog
54 #define LOGCRIT         LOG_CRIT
55 #define LOGERR          LOG_ERR
56 #define LOGWARNING      LOG_WARNING
57 #define EOL
58 #else
59 #define SYSLOG          fprintf
60 #define LOGCRIT         stderr
61 #define LOGERR          stderr
62 #define LOGWARNING      stderr
63 #define EOL     "\n"
64 #endif /* ndef BTHIDCONTROL */
65
66 #define NAMELESS_DEVICE "No Name"
67
68 #include "bthid_config.h"
69
70         int     yylex           (void);
71         void    yyerror         (char const *);
72 static  int32_t check_hid_device(hid_device_p hid_device);
73 static  void    free_hid_device (hid_device_p hid_device);
74
75 extern  FILE                    *yyin;
76 extern  int                      yylineno;
77         char const              *config_file = BTHIDD_CONFFILE;
78         char const              *hids_file   = BTHIDD_HIDSFILE;
79
80 static  char                     buffer[1024];
81 static  int32_t                  hid_descriptor_size;
82 static  hid_device_t            *hid_device = NULL;
83 static  LIST_HEAD(, hid_device)  hid_devices;
84
85 %}
86
87 %union {
88         bdaddr_t        bdaddr;
89         int32_t         num;
90         char            *string;
91 }
92
93 %token <bdaddr> T_BDADDRSTRING
94 %token <num>    T_HEXBYTE
95 %token <num>    T_HEXWORD
96 %token <string> T_STRING
97 %token T_NAME
98 %token T_DEVICE T_BDADDR T_VENDOR_ID T_PRODUCT_ID T_VERSION T_CONTROL_PSM
99 %token T_INTERRUPT_PSM T_RECONNECT_INITIATE T_BATTERY_POWER
100 %token T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
101 %token T_TRUE T_FALSE T_ERROR
102
103 %%
104
105 config:         line
106                 | config line
107                 ;
108
109 line:           T_DEVICE
110                         {
111                         hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
112                         if (hid_device == NULL) {
113                                 SYSLOG(LOGCRIT, "Could not allocate new " \
114                                                 "config entry" EOL);
115                                 YYABORT;
116                         }
117
118                         hid_device->new_device = 1;
119                         }
120                 '{' options '}'
121                         {
122                         if (check_hid_device(hid_device))
123                                 LIST_INSERT_HEAD(&hid_devices,hid_device,next);
124                         else
125                                 free_hid_device(hid_device);
126
127                         hid_device = NULL;
128                         }
129                 ;
130
131 options:        option ';'
132                 | options option ';'
133                 ;
134
135 option:         bdaddr
136                 | name
137                 | vendor_id
138                 | product_id
139                 | version
140                 | control_psm
141                 | interrupt_psm
142                 | reconnect_initiate
143                 | battery_power
144                 | normally_connectable
145                 | hid_descriptor
146                 | parser_error
147                 ;
148
149 bdaddr:         T_BDADDR T_BDADDRSTRING
150                         {
151                         memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
152                         }
153                 ;
154
155 name:           T_NAME T_STRING
156                         {
157                         if (hid_device->name != NULL) {
158                                 free(hid_device->name);
159                                 hid_device->name = NULL;
160                         }
161
162                         if (strcmp($2, NAMELESS_DEVICE)) {
163                                 hid_device->name = strdup($2);
164                                 if (hid_device->name == NULL) {
165                                         SYSLOG(LOGCRIT, "Could not allocate new " \
166                                                         "device name" EOL);
167                                         YYABORT;
168                                 }
169                         }
170                         }
171                 ;
172
173 vendor_id:      T_VENDOR_ID T_HEXWORD
174                         {
175                         hid_device->vendor_id = $2;
176                         }
177                 ;
178
179 product_id:     T_PRODUCT_ID T_HEXWORD
180                         {
181                         hid_device->product_id = $2;
182                         }
183                 ;
184
185 version:        T_VERSION T_HEXWORD
186                         {
187                         hid_device->version = $2;
188                         }
189                 ;
190
191 control_psm:    T_CONTROL_PSM T_HEXBYTE
192                         {
193                         hid_device->control_psm = $2;
194                         }
195                 ;
196
197 interrupt_psm:  T_INTERRUPT_PSM T_HEXBYTE
198                         {
199                         hid_device->interrupt_psm = $2;
200                         }
201                 ;
202
203 reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
204                         {
205                         hid_device->reconnect_initiate = 1;
206                         }
207                 | T_RECONNECT_INITIATE T_FALSE
208                         {
209                         hid_device->reconnect_initiate = 0;
210                         }
211                 ;
212
213 battery_power:  T_BATTERY_POWER T_TRUE
214                         {
215                         hid_device->battery_power = 1;
216                         }
217                 | T_BATTERY_POWER T_FALSE
218                         {
219                         hid_device->battery_power = 0;
220                         }
221                 ;
222
223 normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
224                         {
225                         hid_device->normally_connectable = 1;
226                         }
227                 | T_NORMALLY_CONNECTABLE T_FALSE
228                         {
229                         hid_device->normally_connectable = 0;
230                         }
231                 ;
232
233 hid_descriptor: T_HID_DESCRIPTOR        
234                         {
235                         hid_descriptor_size = 0;
236                         }
237                 '{' hid_descriptor_bytes '}'
238                         {
239                         if (hid_device->desc != NULL)
240                                 hid_dispose_report_desc(hid_device->desc);
241
242                         hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
243                         if (hid_device->desc == NULL) {
244                                 SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
245                                 YYABORT;
246                         }
247                         }
248                 ;
249
250 hid_descriptor_bytes: hid_descriptor_byte
251                 | hid_descriptor_bytes hid_descriptor_byte
252                 ;
253
254 hid_descriptor_byte: T_HEXBYTE
255                         {
256                         if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
257                                 SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
258                                 YYABORT;
259                         }
260
261                         buffer[hid_descriptor_size ++] = $1;
262                         }
263                 ;
264
265 parser_error:   T_ERROR
266                         {
267                                 YYABORT;
268                         }
269
270 %%
271
272 /* Display parser error message */
273 void
274 yyerror(char const *message)
275 {
276         SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 
277 }
278
279 /* Re-read config file */
280 int32_t
281 read_config_file(void)
282 {
283         int32_t e;
284
285         if (config_file == NULL) {
286                 SYSLOG(LOGERR, "Unknown config file name!" EOL);
287                 return (-1);
288         }
289
290         if ((yyin = fopen(config_file, "r")) == NULL) {
291                 SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
292                                 config_file, strerror(errno), errno);
293                 return (-1);
294         }
295
296         clean_config();
297         if (yyparse() < 0) {
298                 SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
299                                 config_file);
300                 e = -1;
301         } else
302                 e = 0;
303
304         fclose(yyin);
305         yyin = NULL;
306
307         return (e);
308 }
309
310 /* Clean config */
311 void
312 clean_config(void)
313 {
314         while (!LIST_EMPTY(&hid_devices)) {
315                 hid_device_p    d = LIST_FIRST(&hid_devices);
316
317                 LIST_REMOVE(d, next);
318                 free_hid_device(d);
319         }
320 }
321
322 /* Lookup config entry */
323 hid_device_p
324 get_hid_device(bdaddr_p bdaddr)
325 {
326         hid_device_p    d;
327
328         LIST_FOREACH(d, &hid_devices, next)
329                 if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
330                         break;
331
332         return (d);
333 }
334
335 /* Get next config entry */
336 hid_device_p
337 get_next_hid_device(hid_device_p d)
338 {
339         return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
340 }
341
342 /* Print config entry */
343 void
344 print_hid_device(hid_device_p d, FILE *f)
345 {
346         /* XXX FIXME hack! */
347         struct report_desc {
348                 unsigned int    size;
349                 unsigned char   data[1];
350         };
351         /* XXX FIXME hack! */
352
353         struct report_desc      *desc = (struct report_desc *) d->desc;
354         uint32_t                 i;
355
356         fprintf(f,
357 "device {\n"                                    \
358 "       bdaddr                  %s;\n"          \
359 "       name                    \"%s\";\n"      \
360 "       vendor_id               0x%04x;\n"      \
361 "       product_id              0x%04x;\n"      \
362 "       version                 0x%04x;\n"      \
363 "       control_psm             0x%x;\n"        \
364 "       interrupt_psm           0x%x;\n"        \
365 "       reconnect_initiate      %s;\n"          \
366 "       battery_power           %s;\n"          \
367 "       normally_connectable    %s;\n"          \
368 "       hid_descriptor          {",
369                 bt_ntoa(&d->bdaddr, NULL),
370                 (d->name != NULL)? d->name : NAMELESS_DEVICE,
371                 d->vendor_id, d->product_id, d->version,
372                 d->control_psm, d->interrupt_psm,
373                 d->reconnect_initiate? "true" : "false",
374                 d->battery_power? "true" : "false",
375                 d->normally_connectable? "true" : "false");
376  
377         for (i = 0; i < desc->size; i ++) {
378                         if ((i % 8) == 0)
379                                 fprintf(f, "\n          ");
380  
381                         fprintf(f, "0x%2.2x ", desc->data[i]);
382         }
383                 
384         fprintf(f,
385 "\n"            \
386 "       };\n"   \
387 "}\n");
388 }
389
390 /* Check config entry */
391 static int32_t
392 check_hid_device(hid_device_p d)
393 {
394         hid_data_t      hd;
395         hid_item_t      hi;
396         int32_t         page, mdepth;
397
398         if (get_hid_device(&d->bdaddr) != NULL) {
399                 SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
400                                 bt_ntoa(&d->bdaddr, NULL));
401                 return (0);
402         }
403
404         if (d->control_psm == 0) {
405                 SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
406                 return (0);
407         }
408
409         if (d->interrupt_psm == 0) {
410                 SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
411                 return (0);
412         }
413
414         if (d->desc == NULL) {
415                 SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
416                 return (0);
417         }
418
419         mdepth = 0;
420
421         /* XXX somehow need to make sure descriptor is valid */
422         for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
423                 switch (hi.kind) {
424                 case hid_collection:
425                         if (mdepth != 0)
426                                 mdepth++;
427                         else if (hi.collection == 1 &&
428                              hi.usage ==
429                               HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))
430                                 mdepth++;
431                         break;
432                 case hid_endcollection:
433                         if (mdepth != 0)
434                                 mdepth--;
435                         break;
436                 case hid_output:
437                 case hid_feature:
438                         break;
439
440                 case hid_input:
441                         /* Check if the device may send keystrokes */
442                         page = HID_PAGE(hi.usage);
443                         if (page == HUP_KEYBOARD)
444                                 d->keyboard = 1;
445                         if (page == HUP_CONSUMER &&
446                             (hi.flags & (HIO_CONST|HIO_RELATIVE)) == 0)
447                                 d->has_cons = 1;
448                         /* Check if the device may send relative motion events */
449                         if (mdepth == 0)
450                                 break;
451                         if (hi.usage ==
452                              HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) &&
453                             (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
454                                 d->mouse = 1;
455                         if (hi.usage ==
456                              HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) &&
457                             (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
458                                 d->mouse = 1;
459                         if (hi.usage ==
460                              HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL) &&
461                             (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
462                                 d->has_wheel = 1;
463                         if (hi.usage ==
464                             HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN) &&
465                             (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
466                                 d->has_hwheel = 1;
467                         break;
468                 }
469         }
470         hid_end_parse(hd);
471
472         return (1);
473 }
474
475 /* Free config entry */
476 static void
477 free_hid_device(hid_device_p d)
478 {
479         if (d->desc != NULL)
480                 hid_dispose_report_desc(d->desc);
481
482         free(d->name);
483         memset(d, 0, sizeof(*d));
484         free(d);
485 }
486
487 /* Re-read hids file */
488 int32_t
489 read_hids_file(void)
490 {
491         FILE            *f;
492         hid_device_t    *d;
493         char            *line;
494         bdaddr_t         bdaddr;
495         int32_t          lineno;
496
497         if (hids_file == NULL) {
498                 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
499                 return (-1);
500         }
501
502         if ((f = fopen(hids_file, "r")) == NULL) {
503                 if (errno == ENOENT)
504                         return (0);
505
506                 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
507                         hids_file, strerror(errno), errno);
508                 return (-1);
509         }
510
511         for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
512                 if ((line = strtok(buffer, "\r\n\t ")) == NULL)
513                         continue; /* ignore empty lines */
514
515                 if (!bt_aton(line, &bdaddr)) {
516                         SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
517                                 "%s:%d" EOL, hids_file, lineno);
518                         continue;
519                 }
520
521                 if ((d = get_hid_device(&bdaddr)) != NULL)
522                         d->new_device = 0;
523         }
524
525         fclose(f);
526
527         return (0);
528 }
529
530 /* Write hids file */
531 int32_t
532 write_hids_file(void)
533 {
534         char             path[PATH_MAX];
535         FILE            *f;
536         hid_device_t    *d;
537
538         if (hids_file == NULL) {
539                 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
540                 return (-1);
541         }
542
543         snprintf(path, sizeof(path), "%s.new", hids_file);
544
545         if ((f = fopen(path, "w")) == NULL) {
546                 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
547                         path, strerror(errno), errno);
548                 return (-1);
549         }
550
551         LIST_FOREACH(d, &hid_devices, next)
552                 if (!d->new_device)
553                         fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
554
555         fclose(f);
556
557         if (rename(path, hids_file) < 0) {
558                 SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
559                         "%s (%d)" EOL, path, hids_file, strerror(errno), errno);
560                 unlink(path);
561                 return (-1);
562         }
563
564         return (0);
565 }
566