cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

qemu-bridge-helper.c (12152B)


      1/*
      2 * QEMU Bridge Helper
      3 *
      4 * Copyright IBM, Corp. 2011
      5 *
      6 * Authors:
      7 * Anthony Liguori   <aliguori@us.ibm.com>
      8 * Richa Marwaha     <rmarwah@linux.vnet.ibm.com>
      9 * Corey Bryant      <coreyb@linux.vnet.ibm.com>
     10 *
     11 * This work is licensed under the terms of the GNU GPL, version 2.  See
     12 * the COPYING file in the top-level directory.
     13 */
     14
     15/*
     16 * Known shortcomings:
     17 * - There is no manual page
     18 * - The syntax of the ACL file is not documented anywhere
     19 * - parse_acl_file() doesn't report fopen() failure properly, fails
     20 *   to check ferror() after fgets() failure, arbitrarily truncates
     21 *   long lines, handles whitespace inconsistently, error messages
     22 *   don't point to the offending file and line, errors in included
     23 *   files are reported, but otherwise ignored, ...
     24 */
     25
     26#include "qemu/osdep.h"
     27
     28
     29#include <sys/ioctl.h>
     30#include <sys/socket.h>
     31#include <sys/un.h>
     32#include <sys/prctl.h>
     33
     34#include <net/if.h>
     35
     36#include <linux/sockios.h>
     37
     38#ifndef SIOCBRADDIF
     39#include <linux/if_bridge.h>
     40#endif
     41
     42#include "qemu/queue.h"
     43#include "qemu/cutils.h"
     44
     45#include "net/tap-linux.h"
     46
     47#ifdef CONFIG_LIBCAP_NG
     48#include <cap-ng.h>
     49#endif
     50
     51#define DEFAULT_ACL_FILE CONFIG_QEMU_CONFDIR "/bridge.conf"
     52
     53enum {
     54    ACL_ALLOW = 0,
     55    ACL_ALLOW_ALL,
     56    ACL_DENY,
     57    ACL_DENY_ALL,
     58};
     59
     60typedef struct ACLRule {
     61    int type;
     62    char iface[IFNAMSIZ];
     63    QSIMPLEQ_ENTRY(ACLRule) entry;
     64} ACLRule;
     65
     66typedef QSIMPLEQ_HEAD(ACLList, ACLRule) ACLList;
     67
     68static void usage(void)
     69{
     70    fprintf(stderr,
     71            "Usage: qemu-bridge-helper [--use-vnet] --br=bridge --fd=unixfd\n");
     72}
     73
     74static int parse_acl_file(const char *filename, ACLList *acl_list)
     75{
     76    FILE *f;
     77    char line[4096];
     78    ACLRule *acl_rule;
     79
     80    f = fopen(filename, "r");
     81    if (f == NULL) {
     82        return -1;
     83    }
     84
     85    while (fgets(line, sizeof(line), f) != NULL) {
     86        char *ptr = line;
     87        char *cmd, *arg, *argend;
     88
     89        while (g_ascii_isspace(*ptr)) {
     90            ptr++;
     91        }
     92
     93        /* skip comments and empty lines */
     94        if (*ptr == '#' || *ptr == 0) {
     95            continue;
     96        }
     97
     98        cmd = ptr;
     99        arg = strchr(cmd, ' ');
    100        if (arg == NULL) {
    101            arg = strchr(cmd, '\t');
    102        }
    103
    104        if (arg == NULL) {
    105            fprintf(stderr, "Invalid config line:\n  %s\n", line);
    106            goto err;
    107        }
    108
    109        *arg = 0;
    110        arg++;
    111        while (g_ascii_isspace(*arg)) {
    112            arg++;
    113        }
    114
    115        argend = arg + strlen(arg);
    116        while (arg != argend && g_ascii_isspace(*(argend - 1))) {
    117            argend--;
    118        }
    119        *argend = 0;
    120
    121        if (!g_str_equal(cmd, "include") && strlen(arg) >= IFNAMSIZ) {
    122            fprintf(stderr, "name `%s' too long: %zu\n", arg, strlen(arg));
    123            goto err;
    124        }
    125
    126        if (strcmp(cmd, "deny") == 0) {
    127            acl_rule = g_malloc(sizeof(*acl_rule));
    128            if (strcmp(arg, "all") == 0) {
    129                acl_rule->type = ACL_DENY_ALL;
    130            } else {
    131                acl_rule->type = ACL_DENY;
    132                snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg);
    133            }
    134            QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry);
    135        } else if (strcmp(cmd, "allow") == 0) {
    136            acl_rule = g_malloc(sizeof(*acl_rule));
    137            if (strcmp(arg, "all") == 0) {
    138                acl_rule->type = ACL_ALLOW_ALL;
    139            } else {
    140                acl_rule->type = ACL_ALLOW;
    141                snprintf(acl_rule->iface, IFNAMSIZ, "%s", arg);
    142            }
    143            QSIMPLEQ_INSERT_TAIL(acl_list, acl_rule, entry);
    144        } else if (strcmp(cmd, "include") == 0) {
    145            /* ignore errors */
    146            parse_acl_file(arg, acl_list);
    147        } else {
    148            fprintf(stderr, "Unknown command `%s'\n", cmd);
    149            goto err;
    150        }
    151    }
    152
    153    fclose(f);
    154    return 0;
    155
    156err:
    157    fclose(f);
    158    errno = EINVAL;
    159    return -1;
    160
    161}
    162
    163static bool has_vnet_hdr(int fd)
    164{
    165    unsigned int features = 0;
    166
    167    if (ioctl(fd, TUNGETFEATURES, &features) == -1) {
    168        return false;
    169    }
    170
    171    if (!(features & IFF_VNET_HDR)) {
    172        return false;
    173    }
    174
    175    return true;
    176}
    177
    178static void prep_ifreq(struct ifreq *ifr, const char *ifname)
    179{
    180    memset(ifr, 0, sizeof(*ifr));
    181    snprintf(ifr->ifr_name, IFNAMSIZ, "%s", ifname);
    182}
    183
    184static int send_fd(int c, int fd)
    185{
    186    char msgbuf[CMSG_SPACE(sizeof(fd))];
    187    struct msghdr msg = {
    188        .msg_control = msgbuf,
    189        .msg_controllen = sizeof(msgbuf),
    190    };
    191    struct cmsghdr *cmsg;
    192    struct iovec iov;
    193    char req[1] = { 0x00 };
    194
    195    cmsg = CMSG_FIRSTHDR(&msg);
    196    cmsg->cmsg_level = SOL_SOCKET;
    197    cmsg->cmsg_type = SCM_RIGHTS;
    198    cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
    199    msg.msg_controllen = cmsg->cmsg_len;
    200
    201    iov.iov_base = req;
    202    iov.iov_len = sizeof(req);
    203
    204    msg.msg_iov = &iov;
    205    msg.msg_iovlen = 1;
    206    memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
    207
    208    return sendmsg(c, &msg, 0);
    209}
    210
    211#ifdef CONFIG_LIBCAP_NG
    212static int drop_privileges(void)
    213{
    214    /* clear all capabilities */
    215    capng_clear(CAPNG_SELECT_BOTH);
    216
    217    if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
    218                     CAP_NET_ADMIN) < 0) {
    219        return -1;
    220    }
    221
    222    /* change to calling user's real uid and gid, retaining supplemental
    223     * groups and CAP_NET_ADMIN */
    224    if (capng_change_id(getuid(), getgid(), CAPNG_CLEAR_BOUNDING)) {
    225        return -1;
    226    }
    227
    228    return 0;
    229}
    230#endif
    231
    232int main(int argc, char **argv)
    233{
    234    struct ifreq ifr;
    235#ifndef SIOCBRADDIF
    236    unsigned long ifargs[4];
    237#endif
    238    int ifindex;
    239    int fd = -1, ctlfd = -1, unixfd = -1;
    240    int use_vnet = 0;
    241    int mtu;
    242    const char *bridge = NULL;
    243    char iface[IFNAMSIZ];
    244    int index;
    245    ACLRule *acl_rule;
    246    ACLList acl_list;
    247    int access_allowed, access_denied;
    248    int ret = EXIT_SUCCESS;
    249    g_autofree char *acl_file = NULL;
    250
    251#ifdef CONFIG_LIBCAP_NG
    252    /* if we're run from an suid binary, immediately drop privileges preserving
    253     * cap_net_admin */
    254    if (geteuid() == 0 && getuid() != geteuid()) {
    255        if (drop_privileges() == -1) {
    256            fprintf(stderr, "failed to drop privileges\n");
    257            return 1;
    258        }
    259    }
    260#endif
    261
    262    qemu_init_exec_dir(argv[0]);
    263
    264    /* parse arguments */
    265    for (index = 1; index < argc; index++) {
    266        if (strcmp(argv[index], "--use-vnet") == 0) {
    267            use_vnet = 1;
    268        } else if (strncmp(argv[index], "--br=", 5) == 0) {
    269            bridge = &argv[index][5];
    270        } else if (strncmp(argv[index], "--fd=", 5) == 0) {
    271            unixfd = atoi(&argv[index][5]);
    272        } else {
    273            usage();
    274            return EXIT_FAILURE;
    275        }
    276    }
    277
    278    if (bridge == NULL || unixfd == -1) {
    279        usage();
    280        return EXIT_FAILURE;
    281    }
    282    if (strlen(bridge) >= IFNAMSIZ) {
    283        fprintf(stderr, "name `%s' too long: %zu\n", bridge, strlen(bridge));
    284        return EXIT_FAILURE;
    285    }
    286
    287    /* parse default acl file */
    288    QSIMPLEQ_INIT(&acl_list);
    289    acl_file = get_relocated_path(DEFAULT_ACL_FILE);
    290    if (parse_acl_file(acl_file, &acl_list) == -1) {
    291        fprintf(stderr, "failed to parse default acl file `%s'\n",
    292                acl_file);
    293        ret = EXIT_FAILURE;
    294        goto cleanup;
    295    }
    296
    297    /* validate bridge against acl -- default policy is to deny
    298     * according acl policy if we have a deny and allow both
    299     * then deny should always win over allow
    300     */
    301    access_allowed = 0;
    302    access_denied = 0;
    303    QSIMPLEQ_FOREACH(acl_rule, &acl_list, entry) {
    304        switch (acl_rule->type) {
    305        case ACL_ALLOW_ALL:
    306            access_allowed = 1;
    307            break;
    308        case ACL_ALLOW:
    309            if (strcmp(bridge, acl_rule->iface) == 0) {
    310                access_allowed = 1;
    311            }
    312            break;
    313        case ACL_DENY_ALL:
    314            access_denied = 1;
    315            break;
    316        case ACL_DENY:
    317            if (strcmp(bridge, acl_rule->iface) == 0) {
    318                access_denied = 1;
    319            }
    320            break;
    321        }
    322    }
    323
    324    if ((access_allowed == 0) || (access_denied == 1)) {
    325        fprintf(stderr, "access denied by acl file\n");
    326        ret = EXIT_FAILURE;
    327        goto cleanup;
    328    }
    329
    330    /* open a socket to use to control the network interfaces */
    331    ctlfd = socket(AF_INET, SOCK_STREAM, 0);
    332    if (ctlfd == -1) {
    333        fprintf(stderr, "failed to open control socket: %s\n", strerror(errno));
    334        ret = EXIT_FAILURE;
    335        goto cleanup;
    336    }
    337
    338    /* open the tap device */
    339    fd = open("/dev/net/tun", O_RDWR);
    340    if (fd == -1) {
    341        fprintf(stderr, "failed to open /dev/net/tun: %s\n", strerror(errno));
    342        ret = EXIT_FAILURE;
    343        goto cleanup;
    344    }
    345
    346    /* request a tap device, disable PI, and add vnet header support if
    347     * requested and it's available. */
    348    prep_ifreq(&ifr, "tap%d");
    349    ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
    350    if (use_vnet && has_vnet_hdr(fd)) {
    351        ifr.ifr_flags |= IFF_VNET_HDR;
    352    }
    353
    354    if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
    355        fprintf(stderr, "failed to create tun device: %s\n", strerror(errno));
    356        ret = EXIT_FAILURE;
    357        goto cleanup;
    358    }
    359
    360    /* save tap device name */
    361    snprintf(iface, sizeof(iface), "%s", ifr.ifr_name);
    362
    363    /* get the mtu of the bridge */
    364    prep_ifreq(&ifr, bridge);
    365    if (ioctl(ctlfd, SIOCGIFMTU, &ifr) == -1) {
    366        fprintf(stderr, "failed to get mtu of bridge `%s': %s\n",
    367                bridge, strerror(errno));
    368        ret = EXIT_FAILURE;
    369        goto cleanup;
    370    }
    371
    372    /* save mtu */
    373    mtu = ifr.ifr_mtu;
    374
    375    /* set the mtu of the interface based on the bridge */
    376    prep_ifreq(&ifr, iface);
    377    ifr.ifr_mtu = mtu;
    378    if (ioctl(ctlfd, SIOCSIFMTU, &ifr) == -1) {
    379        fprintf(stderr, "failed to set mtu of device `%s' to %d: %s\n",
    380                iface, mtu, strerror(errno));
    381        ret = EXIT_FAILURE;
    382        goto cleanup;
    383    }
    384
    385    /* Linux uses the lowest enslaved MAC address as the MAC address of
    386     * the bridge.  Set MAC address to a high value so that it doesn't
    387     * affect the MAC address of the bridge.
    388     */
    389    if (ioctl(ctlfd, SIOCGIFHWADDR, &ifr) < 0) {
    390        fprintf(stderr, "failed to get MAC address of device `%s': %s\n",
    391                iface, strerror(errno));
    392        ret = EXIT_FAILURE;
    393        goto cleanup;
    394    }
    395    ifr.ifr_hwaddr.sa_data[0] = 0xFE;
    396    if (ioctl(ctlfd, SIOCSIFHWADDR, &ifr) < 0) {
    397        fprintf(stderr, "failed to set MAC address of device `%s': %s\n",
    398                iface, strerror(errno));
    399        ret = EXIT_FAILURE;
    400        goto cleanup;
    401    }
    402
    403    /* add the interface to the bridge */
    404    prep_ifreq(&ifr, bridge);
    405    ifindex = if_nametoindex(iface);
    406#ifndef SIOCBRADDIF
    407    ifargs[0] = BRCTL_ADD_IF;
    408    ifargs[1] = ifindex;
    409    ifargs[2] = 0;
    410    ifargs[3] = 0;
    411    ifr.ifr_data = (void *)ifargs;
    412    ret = ioctl(ctlfd, SIOCDEVPRIVATE, &ifr);
    413#else
    414    ifr.ifr_ifindex = ifindex;
    415    ret = ioctl(ctlfd, SIOCBRADDIF, &ifr);
    416#endif
    417    if (ret == -1) {
    418        fprintf(stderr, "failed to add interface `%s' to bridge `%s': %s\n",
    419                iface, bridge, strerror(errno));
    420        ret = EXIT_FAILURE;
    421        goto cleanup;
    422    }
    423
    424    /* bring the interface up */
    425    prep_ifreq(&ifr, iface);
    426    if (ioctl(ctlfd, SIOCGIFFLAGS, &ifr) == -1) {
    427        fprintf(stderr, "failed to get interface flags for `%s': %s\n",
    428                iface, strerror(errno));
    429        ret = EXIT_FAILURE;
    430        goto cleanup;
    431    }
    432
    433    ifr.ifr_flags |= IFF_UP;
    434    if (ioctl(ctlfd, SIOCSIFFLAGS, &ifr) == -1) {
    435        fprintf(stderr, "failed to bring up interface `%s': %s\n",
    436                iface, strerror(errno));
    437        ret = EXIT_FAILURE;
    438        goto cleanup;
    439    }
    440
    441    /* write fd to the domain socket */
    442    if (send_fd(unixfd, fd) == -1) {
    443        fprintf(stderr, "failed to write fd to unix socket: %s\n",
    444                strerror(errno));
    445        ret = EXIT_FAILURE;
    446        goto cleanup;
    447    }
    448
    449    /* ... */
    450
    451    /* profit! */
    452
    453cleanup:
    454    if (fd >= 0) {
    455        close(fd);
    456    }
    457    if (ctlfd >= 0) {
    458        close(ctlfd);
    459    }
    460    while ((acl_rule = QSIMPLEQ_FIRST(&acl_list)) != NULL) {
    461        QSIMPLEQ_REMOVE_HEAD(&acl_list, entry);
    462        g_free(acl_rule);
    463    }
    464
    465    return ret;
    466}