Blame usbredirparser/usbredirfilter.c

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
}