|
Packit |
9795e1 |
/* usbredirfilter.h usb redirection filter header
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
Copyright 2012 Red Hat, Inc.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
Red Hat Authors:
|
|
Packit |
9795e1 |
Hans de Goede <hdegoede@redhat.com>
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
This library is free software; you can redistribute it and/or
|
|
Packit |
9795e1 |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
9795e1 |
License as published by the Free Software Foundation; either
|
|
Packit |
9795e1 |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
This library is distributed in the hope that it will be useful,
|
|
Packit |
9795e1 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
9795e1 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
9795e1 |
Lesser General Public License for more details.
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
9795e1 |
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
9795e1 |
*/
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#include <stdlib.h>
|
|
Packit |
9795e1 |
#include <string.h>
|
|
Packit |
9795e1 |
#include <errno.h>
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#ifdef WIN32
|
|
Packit |
9795e1 |
#include "strtok_r.h"
|
|
Packit |
9795e1 |
#define strtok_r glibc_strtok_r
|
|
Packit |
9795e1 |
#endif
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
#include "usbredirfilter.h"
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirfilter_string_to_rules(
|
|
Packit |
9795e1 |
const char *filter_str, const char *token_sep, const char *rule_sep,
|
|
Packit |
9795e1 |
struct usbredirfilter_rule **rules_ret, int *rules_count_ret)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
char *rule, *rule_saveptr, *token, *token_saveptr, *ep;
|
|
Packit |
9795e1 |
struct usbredirfilter_rule *rules = NULL;
|
|
Packit |
9795e1 |
int i, rules_count, *values, ret = 0;
|
|
Packit |
9795e1 |
char *buf = NULL;
|
|
Packit |
9795e1 |
const char *r;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
*rules_ret = NULL;
|
|
Packit |
9795e1 |
*rules_count_ret = 0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Figure out how much rules there are in the file, so we know how
|
|
Packit |
9795e1 |
much memory we must allocate for the rules array.
|
|
Packit |
9795e1 |
Note this will come up with a slightly too large number if there are
|
|
Packit |
9795e1 |
empty rule strings in the set. */
|
|
Packit |
9795e1 |
r = filter_str;
|
|
Packit |
9795e1 |
rules_count = 0;
|
|
Packit |
9795e1 |
while (r) {
|
|
Packit |
9795e1 |
r = strchr(r, rule_sep[0]);
|
|
Packit |
9795e1 |
if (r)
|
|
Packit |
9795e1 |
r++;
|
|
Packit |
9795e1 |
rules_count++;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
rules = calloc(rules_count, sizeof(struct usbredirfilter_rule));
|
|
Packit |
9795e1 |
if (!rules)
|
|
Packit |
9795e1 |
return -ENOMEM;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Make a copy since strtok mangles the string */
|
|
Packit |
9795e1 |
buf = strdup(filter_str);
|
|
Packit |
9795e1 |
if (!buf) {
|
|
Packit |
9795e1 |
ret = -ENOMEM;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* And actually parse the string */
|
|
Packit |
9795e1 |
rules_count = 0;
|
|
Packit |
9795e1 |
rule = strtok_r(buf, rule_sep, &rule_saveptr);
|
|
Packit |
9795e1 |
while (rule) {
|
|
Packit |
9795e1 |
/* We treat the filter rule as an array of ints for easier parsing */
|
|
Packit |
9795e1 |
values = (int *)&rules[rules_count];
|
|
Packit |
9795e1 |
token = strtok_r(rule, token_sep, &token_saveptr);
|
|
Packit |
9795e1 |
for (i = 0; i < 5 && token; i++) {
|
|
Packit |
9795e1 |
values[i] = strtol(token, &ep, 0);
|
|
Packit |
9795e1 |
if (*ep)
|
|
Packit |
9795e1 |
break;
|
|
Packit |
9795e1 |
token = strtok_r(NULL, token_sep, &token_saveptr);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
if (i != 5 || token != NULL ||
|
|
Packit |
9795e1 |
usbredirfilter_verify(&rules[rules_count], 1)) {
|
|
Packit |
9795e1 |
ret = -EINVAL;
|
|
Packit |
9795e1 |
goto leave;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
rules_count++;
|
|
Packit |
9795e1 |
rule = strtok_r(NULL, rule_sep, &rule_saveptr);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
*rules_ret = rules;
|
|
Packit |
9795e1 |
*rules_count_ret = rules_count;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
leave:
|
|
Packit |
9795e1 |
if (ret)
|
|
Packit |
9795e1 |
free(rules);
|
|
Packit |
9795e1 |
free(buf);
|
|
Packit |
9795e1 |
return ret;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
char *usbredirfilter_rules_to_string(const struct usbredirfilter_rule *rules,
|
|
Packit |
9795e1 |
int rules_count, const char *token_sep, const char *rule_sep)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
char *str, *p;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (usbredirfilter_verify(rules, rules_count))
|
|
Packit |
9795e1 |
return NULL;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* We need 28 bytes per rule in the worst case */
|
|
Packit |
9795e1 |
str = malloc(28 * rules_count + 1);
|
|
Packit |
9795e1 |
if (!str)
|
|
Packit |
9795e1 |
return NULL;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
p = str;
|
|
Packit |
9795e1 |
for (i = 0; i < rules_count; i++) {
|
|
Packit |
9795e1 |
if (rules[i].device_class != -1)
|
|
Packit |
9795e1 |
p += sprintf(p, "0x%02x%c", rules[i].device_class, *token_sep);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
p += sprintf(p, "-1%c", *token_sep);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (rules[i].vendor_id != -1)
|
|
Packit |
9795e1 |
p += sprintf(p, "0x%04x%c", rules[i].vendor_id, *token_sep);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
p += sprintf(p, "-1%c", *token_sep);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (rules[i].product_id != -1)
|
|
Packit |
9795e1 |
p += sprintf(p, "0x%04x%c", rules[i].product_id, *token_sep);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
p += sprintf(p, "-1%c", *token_sep);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (rules[i].device_version_bcd != -1)
|
|
Packit |
9795e1 |
p += sprintf(p, "0x%04x%c", rules[i].device_version_bcd, *token_sep);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
p += sprintf(p, "-1%c", *token_sep);
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
p += sprintf(p, "%d%c", rules[i].allow ? 1:0, *rule_sep);
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return str;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
static int usbredirfilter_check1(const struct usbredirfilter_rule *rules,
|
|
Packit |
9795e1 |
int rules_count, uint8_t device_class, uint16_t vendor_id,
|
|
Packit |
9795e1 |
uint16_t product_id, uint16_t device_version_bcd, int default_allow)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < rules_count; i++) {
|
|
Packit |
9795e1 |
if ((rules[i].device_class == -1 ||
|
|
Packit |
9795e1 |
rules[i].device_class == device_class) &&
|
|
Packit |
9795e1 |
(rules[i].vendor_id == -1 ||
|
|
Packit |
9795e1 |
rules[i].vendor_id == vendor_id) &&
|
|
Packit |
9795e1 |
(rules[i].product_id == -1 ||
|
|
Packit |
9795e1 |
rules[i].product_id == product_id) &&
|
|
Packit |
9795e1 |
(rules[i].device_version_bcd == -1 ||
|
|
Packit |
9795e1 |
rules[i].device_version_bcd == device_version_bcd)) {
|
|
Packit |
9795e1 |
/* Found a match ! */
|
|
Packit |
9795e1 |
return rules[i].allow ? 0 : -EPERM;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return default_allow ? 0 : -ENOENT;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirfilter_check(
|
|
Packit |
9795e1 |
const struct usbredirfilter_rule *rules, int rules_count,
|
|
Packit |
9795e1 |
uint8_t device_class, uint8_t device_subclass, uint8_t device_protocol,
|
|
Packit |
9795e1 |
uint8_t *interface_class, uint8_t *interface_subclass,
|
|
Packit |
9795e1 |
uint8_t *interface_protocol, int interface_count,
|
|
Packit |
9795e1 |
uint16_t vendor_id, uint16_t product_id, uint16_t device_version_bcd,
|
|
Packit |
9795e1 |
int flags)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i, rc, num_skipped=0;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (usbredirfilter_verify(rules, rules_count))
|
|
Packit |
9795e1 |
return -EINVAL;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Check the device_class */
|
|
Packit |
9795e1 |
if (device_class != 0x00 && device_class != 0xef) {
|
|
Packit |
9795e1 |
rc = usbredirfilter_check1(rules, rules_count, device_class,
|
|
Packit |
9795e1 |
vendor_id, product_id, device_version_bcd,
|
|
Packit |
9795e1 |
flags & usbredirfilter_fl_default_allow);
|
|
Packit |
9795e1 |
if (rc)
|
|
Packit |
9795e1 |
return rc;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* Check the interface classes */
|
|
Packit |
9795e1 |
for (i = 0; i < interface_count; i++) {
|
|
Packit |
9795e1 |
if (!(flags & usbredirfilter_fl_dont_skip_non_boot_hid) &&
|
|
Packit |
9795e1 |
interface_count > 1 && interface_class[i] == 0x03 &&
|
|
Packit |
9795e1 |
interface_subclass[i] == 0x00 && interface_protocol[i] == 0x00) {
|
|
Packit |
9795e1 |
num_skipped++;
|
|
Packit |
9795e1 |
continue;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
rc = usbredirfilter_check1(rules, rules_count, interface_class[i],
|
|
Packit |
9795e1 |
vendor_id, product_id, device_version_bcd,
|
|
Packit |
9795e1 |
flags & usbredirfilter_fl_default_allow);
|
|
Packit |
9795e1 |
if (rc)
|
|
Packit |
9795e1 |
return rc;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
/* If all interfaces were skipped, then force check on that device,
|
|
Packit |
9795e1 |
* by recursively calling this function with a flag that forbids
|
|
Packit |
9795e1 |
* skipping (usbredirfilter_fl_dont_skip_non_boot_hid)
|
|
Packit |
9795e1 |
*/
|
|
Packit |
9795e1 |
if (interface_count > 0 && num_skipped == interface_count) {
|
|
Packit |
9795e1 |
rc = usbredirfilter_check(rules, rules_count,
|
|
Packit |
9795e1 |
device_class, device_subclass, device_protocol,
|
|
Packit |
9795e1 |
interface_class, interface_subclass,
|
|
Packit |
9795e1 |
interface_protocol, interface_count,
|
|
Packit |
9795e1 |
vendor_id, product_id, device_version_bcd,
|
|
Packit |
9795e1 |
flags | usbredirfilter_fl_dont_skip_non_boot_hid);
|
|
Packit |
9795e1 |
return rc;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
return 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
int usbredirfilter_verify(
|
|
Packit |
9795e1 |
const struct usbredirfilter_rule *rules, int rules_count)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < rules_count; i++) {
|
|
Packit |
9795e1 |
if (rules[i].device_class < -1 || rules[i].device_class > 255)
|
|
Packit |
9795e1 |
return -EINVAL;
|
|
Packit |
9795e1 |
if (rules[i].vendor_id < -1 || rules[i].vendor_id > 65535)
|
|
Packit |
9795e1 |
return -EINVAL;
|
|
Packit |
9795e1 |
if (rules[i].product_id < -1 || rules[i].product_id > 65535)
|
|
Packit |
9795e1 |
return -EINVAL;
|
|
Packit |
9795e1 |
if (rules[i].device_version_bcd < -1 ||
|
|
Packit |
9795e1 |
rules[i].device_version_bcd > 65535)
|
|
Packit |
9795e1 |
return -EINVAL;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
return 0;
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
void usbredirfilter_print(
|
|
Packit |
9795e1 |
const struct usbredirfilter_rule *rules, int rules_count, FILE *out)
|
|
Packit |
9795e1 |
{
|
|
Packit |
9795e1 |
int i;
|
|
Packit |
9795e1 |
char device_class[16], vendor[16], product[16], version[16];
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
for (i = 0; i < rules_count; i++) {
|
|
Packit |
9795e1 |
if (rules[i].device_class != -1)
|
|
Packit |
9795e1 |
sprintf(device_class, " %02x", rules[i].device_class);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
strcpy(device_class, "ANY");
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (rules[i].vendor_id != -1)
|
|
Packit |
9795e1 |
sprintf(vendor, "%04x", rules[i].vendor_id);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
strcpy(vendor, " ANY");
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (rules[i].product_id != -1)
|
|
Packit |
9795e1 |
sprintf(product, "%04x", rules[i].product_id);
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
strcpy(product, " ANY");
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
if (rules[i].device_version_bcd != -1)
|
|
Packit |
9795e1 |
sprintf(version, "%2d.%02d",
|
|
Packit |
9795e1 |
((rules[i].device_version_bcd & 0xf000) >> 12) * 10 +
|
|
Packit |
9795e1 |
((rules[i].device_version_bcd & 0x0f00) >> 8),
|
|
Packit |
9795e1 |
((rules[i].device_version_bcd & 0x00f0) >> 4) * 10 +
|
|
Packit |
9795e1 |
((rules[i].device_version_bcd & 0x000f)));
|
|
Packit |
9795e1 |
else
|
|
Packit |
9795e1 |
strcpy(version, " ANY");
|
|
Packit |
9795e1 |
|
|
Packit |
9795e1 |
fprintf(out, "Class %s ID %s:%s Version %s %s\n", device_class, vendor,
|
|
Packit |
9795e1 |
product, version, rules[i].allow ? "Allow":"Block");
|
|
Packit |
9795e1 |
}
|
|
Packit |
9795e1 |
}
|