qemu/block/ssh.c
<<
>>
Prefs
   1/*
   2 * Secure Shell (ssh) backend for QEMU.
   3 *
   4 * Copyright (C) 2013 Red Hat Inc., Richard W.M. Jones <rjones@redhat.com>
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26
  27#include <libssh2.h>
  28#include <libssh2_sftp.h>
  29
  30#include "block/block_int.h"
  31#include "qapi/error.h"
  32#include "qemu/error-report.h"
  33#include "qemu/cutils.h"
  34#include "qemu/sockets.h"
  35#include "qemu/uri.h"
  36#include "qapi-visit.h"
  37#include "qapi/qmp/qint.h"
  38#include "qapi/qmp/qstring.h"
  39#include "qapi/qobject-input-visitor.h"
  40#include "qapi/qobject-output-visitor.h"
  41
  42/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in
  43 * this block driver code.
  44 *
  45 * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself.  Note
  46 * that this requires that libssh2 was specially compiled with the
  47 * `./configure --enable-debug' option, so most likely you will have
  48 * to compile it yourself.  The meaning of <bitmask> is described
  49 * here: http://www.libssh2.org/libssh2_trace.html
  50 */
  51#define DEBUG_SSH     0
  52#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
  53
  54#define DPRINTF(fmt, ...)                           \
  55    do {                                            \
  56        if (DEBUG_SSH) {                            \
  57            fprintf(stderr, "ssh: %-15s " fmt "\n", \
  58                    __func__, ##__VA_ARGS__);       \
  59        }                                           \
  60    } while (0)
  61
  62typedef struct BDRVSSHState {
  63    /* Coroutine. */
  64    CoMutex lock;
  65
  66    /* SSH connection. */
  67    int sock;                         /* socket */
  68    LIBSSH2_SESSION *session;         /* ssh session */
  69    LIBSSH2_SFTP *sftp;               /* sftp session */
  70    LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */
  71
  72    /* See ssh_seek() function below. */
  73    int64_t offset;
  74    bool offset_op_read;
  75
  76    /* File attributes at open.  We try to keep the .filesize field
  77     * updated if it changes (eg by writing at the end of the file).
  78     */
  79    LIBSSH2_SFTP_ATTRIBUTES attrs;
  80
  81    InetSocketAddress *inet;
  82
  83    /* Used to warn if 'flush' is not supported. */
  84    bool unsafe_flush_warning;
  85} BDRVSSHState;
  86
  87static void ssh_state_init(BDRVSSHState *s)
  88{
  89    memset(s, 0, sizeof *s);
  90    s->sock = -1;
  91    s->offset = -1;
  92    qemu_co_mutex_init(&s->lock);
  93}
  94
  95static void ssh_state_free(BDRVSSHState *s)
  96{
  97    if (s->sftp_handle) {
  98        libssh2_sftp_close(s->sftp_handle);
  99    }
 100    if (s->sftp) {
 101        libssh2_sftp_shutdown(s->sftp);
 102    }
 103    if (s->session) {
 104        libssh2_session_disconnect(s->session,
 105                                   "from qemu ssh client: "
 106                                   "user closed the connection");
 107        libssh2_session_free(s->session);
 108    }
 109    if (s->sock >= 0) {
 110        close(s->sock);
 111    }
 112}
 113
 114static void GCC_FMT_ATTR(3, 4)
 115session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
 116{
 117    va_list args;
 118    char *msg;
 119
 120    va_start(args, fs);
 121    msg = g_strdup_vprintf(fs, args);
 122    va_end(args);
 123
 124    if (s->session) {
 125        char *ssh_err;
 126        int ssh_err_code;
 127
 128        /* This is not an errno.  See <libssh2.h>. */
 129        ssh_err_code = libssh2_session_last_error(s->session,
 130                                                  &ssh_err, NULL, 0);
 131        error_setg(errp, "%s: %s (libssh2 error code: %d)",
 132                   msg, ssh_err, ssh_err_code);
 133    } else {
 134        error_setg(errp, "%s", msg);
 135    }
 136    g_free(msg);
 137}
 138
 139static void GCC_FMT_ATTR(3, 4)
 140sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
 141{
 142    va_list args;
 143    char *msg;
 144
 145    va_start(args, fs);
 146    msg = g_strdup_vprintf(fs, args);
 147    va_end(args);
 148
 149    if (s->sftp) {
 150        char *ssh_err;
 151        int ssh_err_code;
 152        unsigned long sftp_err_code;
 153
 154        /* This is not an errno.  See <libssh2.h>. */
 155        ssh_err_code = libssh2_session_last_error(s->session,
 156                                                  &ssh_err, NULL, 0);
 157        /* See <libssh2_sftp.h>. */
 158        sftp_err_code = libssh2_sftp_last_error((s)->sftp);
 159
 160        error_setg(errp,
 161                   "%s: %s (libssh2 error code: %d, sftp error code: %lu)",
 162                   msg, ssh_err, ssh_err_code, sftp_err_code);
 163    } else {
 164        error_setg(errp, "%s", msg);
 165    }
 166    g_free(msg);
 167}
 168
 169static void GCC_FMT_ATTR(2, 3)
 170sftp_error_report(BDRVSSHState *s, const char *fs, ...)
 171{
 172    va_list args;
 173
 174    va_start(args, fs);
 175    error_vprintf(fs, args);
 176
 177    if ((s)->sftp) {
 178        char *ssh_err;
 179        int ssh_err_code;
 180        unsigned long sftp_err_code;
 181
 182        /* This is not an errno.  See <libssh2.h>. */
 183        ssh_err_code = libssh2_session_last_error(s->session,
 184                                                  &ssh_err, NULL, 0);
 185        /* See <libssh2_sftp.h>. */
 186        sftp_err_code = libssh2_sftp_last_error((s)->sftp);
 187
 188        error_printf(": %s (libssh2 error code: %d, sftp error code: %lu)",
 189                     ssh_err, ssh_err_code, sftp_err_code);
 190    }
 191
 192    va_end(args);
 193    error_printf("\n");
 194}
 195
 196static int parse_uri(const char *filename, QDict *options, Error **errp)
 197{
 198    URI *uri = NULL;
 199    QueryParams *qp;
 200    char *port_str;
 201    int i;
 202
 203    uri = uri_parse(filename);
 204    if (!uri) {
 205        return -EINVAL;
 206    }
 207
 208    if (strcmp(uri->scheme, "ssh") != 0) {
 209        error_setg(errp, "URI scheme must be 'ssh'");
 210        goto err;
 211    }
 212
 213    if (!uri->server || strcmp(uri->server, "") == 0) {
 214        error_setg(errp, "missing hostname in URI");
 215        goto err;
 216    }
 217
 218    if (!uri->path || strcmp(uri->path, "") == 0) {
 219        error_setg(errp, "missing remote path in URI");
 220        goto err;
 221    }
 222
 223    qp = query_params_parse(uri->query);
 224    if (!qp) {
 225        error_setg(errp, "could not parse query parameters");
 226        goto err;
 227    }
 228
 229    if(uri->user && strcmp(uri->user, "") != 0) {
 230        qdict_put(options, "user", qstring_from_str(uri->user));
 231    }
 232
 233    qdict_put(options, "server.host", qstring_from_str(uri->server));
 234
 235    port_str = g_strdup_printf("%d", uri->port ?: 22);
 236    qdict_put(options, "server.port", qstring_from_str(port_str));
 237    g_free(port_str);
 238
 239    qdict_put(options, "path", qstring_from_str(uri->path));
 240
 241    /* Pick out any query parameters that we understand, and ignore
 242     * the rest.
 243     */
 244    for (i = 0; i < qp->n; ++i) {
 245        if (strcmp(qp->p[i].name, "host_key_check") == 0) {
 246            qdict_put(options, "host_key_check",
 247                      qstring_from_str(qp->p[i].value));
 248        }
 249    }
 250
 251    query_params_free(qp);
 252    uri_free(uri);
 253    return 0;
 254
 255 err:
 256    if (uri) {
 257      uri_free(uri);
 258    }
 259    return -EINVAL;
 260}
 261
 262static bool ssh_has_filename_options_conflict(QDict *options, Error **errp)
 263{
 264    const QDictEntry *qe;
 265
 266    for (qe = qdict_first(options); qe; qe = qdict_next(options, qe)) {
 267        if (!strcmp(qe->key, "host") ||
 268            !strcmp(qe->key, "port") ||
 269            !strcmp(qe->key, "path") ||
 270            !strcmp(qe->key, "user") ||
 271            !strcmp(qe->key, "host_key_check") ||
 272            strstart(qe->key, "server.", NULL))
 273        {
 274            error_setg(errp, "Option '%s' cannot be used with a file name",
 275                       qe->key);
 276            return true;
 277        }
 278    }
 279
 280    return false;
 281}
 282
 283static void ssh_parse_filename(const char *filename, QDict *options,
 284                               Error **errp)
 285{
 286    if (ssh_has_filename_options_conflict(options, errp)) {
 287        return;
 288    }
 289
 290    parse_uri(filename, options, errp);
 291}
 292
 293static int check_host_key_knownhosts(BDRVSSHState *s,
 294                                     const char *host, int port, Error **errp)
 295{
 296    const char *home;
 297    char *knh_file = NULL;
 298    LIBSSH2_KNOWNHOSTS *knh = NULL;
 299    struct libssh2_knownhost *found;
 300    int ret, r;
 301    const char *hostkey;
 302    size_t len;
 303    int type;
 304
 305    hostkey = libssh2_session_hostkey(s->session, &len, &type);
 306    if (!hostkey) {
 307        ret = -EINVAL;
 308        session_error_setg(errp, s, "failed to read remote host key");
 309        goto out;
 310    }
 311
 312    knh = libssh2_knownhost_init(s->session);
 313    if (!knh) {
 314        ret = -EINVAL;
 315        session_error_setg(errp, s,
 316                           "failed to initialize known hosts support");
 317        goto out;
 318    }
 319
 320    home = getenv("HOME");
 321    if (home) {
 322        knh_file = g_strdup_printf("%s/.ssh/known_hosts", home);
 323    } else {
 324        knh_file = g_strdup_printf("/root/.ssh/known_hosts");
 325    }
 326
 327    /* Read all known hosts from OpenSSH-style known_hosts file. */
 328    libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 329
 330    r = libssh2_knownhost_checkp(knh, host, port, hostkey, len,
 331                                 LIBSSH2_KNOWNHOST_TYPE_PLAIN|
 332                                 LIBSSH2_KNOWNHOST_KEYENC_RAW,
 333                                 &found);
 334    switch (r) {
 335    case LIBSSH2_KNOWNHOST_CHECK_MATCH:
 336        /* OK */
 337        DPRINTF("host key OK: %s", found->key);
 338        break;
 339    case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
 340        ret = -EINVAL;
 341        session_error_setg(errp, s,
 342                      "host key does not match the one in known_hosts"
 343                      " (found key %s)", found->key);
 344        goto out;
 345    case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
 346        ret = -EINVAL;
 347        session_error_setg(errp, s, "no host key was found in known_hosts");
 348        goto out;
 349    case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
 350        ret = -EINVAL;
 351        session_error_setg(errp, s,
 352                      "failure matching the host key with known_hosts");
 353        goto out;
 354    default:
 355        ret = -EINVAL;
 356        session_error_setg(errp, s, "unknown error matching the host key"
 357                      " with known_hosts (%d)", r);
 358        goto out;
 359    }
 360
 361    /* known_hosts checking successful. */
 362    ret = 0;
 363
 364 out:
 365    if (knh != NULL) {
 366        libssh2_knownhost_free(knh);
 367    }
 368    g_free(knh_file);
 369    return ret;
 370}
 371
 372static unsigned hex2decimal(char ch)
 373{
 374    if (ch >= '0' && ch <= '9') {
 375        return (ch - '0');
 376    } else if (ch >= 'a' && ch <= 'f') {
 377        return 10 + (ch - 'a');
 378    } else if (ch >= 'A' && ch <= 'F') {
 379        return 10 + (ch - 'A');
 380    }
 381
 382    return -1;
 383}
 384
 385/* Compare the binary fingerprint (hash of host key) with the
 386 * host_key_check parameter.
 387 */
 388static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
 389                               const char *host_key_check)
 390{
 391    unsigned c;
 392
 393    while (len > 0) {
 394        while (*host_key_check == ':')
 395            host_key_check++;
 396        if (!qemu_isxdigit(host_key_check[0]) ||
 397            !qemu_isxdigit(host_key_check[1]))
 398            return 1;
 399        c = hex2decimal(host_key_check[0]) * 16 +
 400            hex2decimal(host_key_check[1]);
 401        if (c - *fingerprint != 0)
 402            return c - *fingerprint;
 403        fingerprint++;
 404        len--;
 405        host_key_check += 2;
 406    }
 407    return *host_key_check - '\0';
 408}
 409
 410static int
 411check_host_key_hash(BDRVSSHState *s, const char *hash,
 412                    int hash_type, size_t fingerprint_len, Error **errp)
 413{
 414    const char *fingerprint;
 415
 416    fingerprint = libssh2_hostkey_hash(s->session, hash_type);
 417    if (!fingerprint) {
 418        session_error_setg(errp, s, "failed to read remote host key");
 419        return -EINVAL;
 420    }
 421
 422    if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
 423                           hash) != 0) {
 424        error_setg(errp, "remote host key does not match host_key_check '%s'",
 425                   hash);
 426        return -EPERM;
 427    }
 428
 429    return 0;
 430}
 431
 432static int check_host_key(BDRVSSHState *s, const char *host, int port,
 433                          const char *host_key_check, Error **errp)
 434{
 435    /* host_key_check=no */
 436    if (strcmp(host_key_check, "no") == 0) {
 437        return 0;
 438    }
 439
 440    /* host_key_check=md5:xx:yy:zz:... */
 441    if (strncmp(host_key_check, "md5:", 4) == 0) {
 442        return check_host_key_hash(s, &host_key_check[4],
 443                                   LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
 444    }
 445
 446    /* host_key_check=sha1:xx:yy:zz:... */
 447    if (strncmp(host_key_check, "sha1:", 5) == 0) {
 448        return check_host_key_hash(s, &host_key_check[5],
 449                                   LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
 450    }
 451
 452    /* host_key_check=yes */
 453    if (strcmp(host_key_check, "yes") == 0) {
 454        return check_host_key_knownhosts(s, host, port, errp);
 455    }
 456
 457    error_setg(errp, "unknown host_key_check setting (%s)", host_key_check);
 458    return -EINVAL;
 459}
 460
 461static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
 462{
 463    int r, ret;
 464    const char *userauthlist;
 465    LIBSSH2_AGENT *agent = NULL;
 466    struct libssh2_agent_publickey *identity;
 467    struct libssh2_agent_publickey *prev_identity = NULL;
 468
 469    userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
 470    if (strstr(userauthlist, "publickey") == NULL) {
 471        ret = -EPERM;
 472        error_setg(errp,
 473                "remote server does not support \"publickey\" authentication");
 474        goto out;
 475    }
 476
 477    /* Connect to ssh-agent and try each identity in turn. */
 478    agent = libssh2_agent_init(s->session);
 479    if (!agent) {
 480        ret = -EINVAL;
 481        session_error_setg(errp, s, "failed to initialize ssh-agent support");
 482        goto out;
 483    }
 484    if (libssh2_agent_connect(agent)) {
 485        ret = -ECONNREFUSED;
 486        session_error_setg(errp, s, "failed to connect to ssh-agent");
 487        goto out;
 488    }
 489    if (libssh2_agent_list_identities(agent)) {
 490        ret = -EINVAL;
 491        session_error_setg(errp, s,
 492                           "failed requesting identities from ssh-agent");
 493        goto out;
 494    }
 495
 496    for(;;) {
 497        r = libssh2_agent_get_identity(agent, &identity, prev_identity);
 498        if (r == 1) {           /* end of list */
 499            break;
 500        }
 501        if (r < 0) {
 502            ret = -EINVAL;
 503            session_error_setg(errp, s,
 504                               "failed to obtain identity from ssh-agent");
 505            goto out;
 506        }
 507        r = libssh2_agent_userauth(agent, user, identity);
 508        if (r == 0) {
 509            /* Authenticated! */
 510            ret = 0;
 511            goto out;
 512        }
 513        /* Failed to authenticate with this identity, try the next one. */
 514        prev_identity = identity;
 515    }
 516
 517    ret = -EPERM;
 518    error_setg(errp, "failed to authenticate using publickey authentication "
 519               "and the identities held by your ssh-agent");
 520
 521 out:
 522    if (agent != NULL) {
 523        /* Note: libssh2 implementation implicitly calls
 524         * libssh2_agent_disconnect if necessary.
 525         */
 526        libssh2_agent_free(agent);
 527    }
 528
 529    return ret;
 530}
 531
 532static QemuOptsList ssh_runtime_opts = {
 533    .name = "ssh",
 534    .head = QTAILQ_HEAD_INITIALIZER(ssh_runtime_opts.head),
 535    .desc = {
 536        {
 537            .name = "host",
 538            .type = QEMU_OPT_STRING,
 539            .help = "Host to connect to",
 540        },
 541        {
 542            .name = "port",
 543            .type = QEMU_OPT_NUMBER,
 544            .help = "Port to connect to",
 545        },
 546        {
 547            .name = "path",
 548            .type = QEMU_OPT_STRING,
 549            .help = "Path of the image on the host",
 550        },
 551        {
 552            .name = "user",
 553            .type = QEMU_OPT_STRING,
 554            .help = "User as which to connect",
 555        },
 556        {
 557            .name = "host_key_check",
 558            .type = QEMU_OPT_STRING,
 559            .help = "Defines how and what to check the host key against",
 560        },
 561    },
 562};
 563
 564static bool ssh_process_legacy_socket_options(QDict *output_opts,
 565                                              QemuOpts *legacy_opts,
 566                                              Error **errp)
 567{
 568    const char *host = qemu_opt_get(legacy_opts, "host");
 569    const char *port = qemu_opt_get(legacy_opts, "port");
 570
 571    if (!host && port) {
 572        error_setg(errp, "port may not be used without host");
 573        return false;
 574    }
 575
 576    if (host) {
 577        qdict_put(output_opts, "server.host", qstring_from_str(host));
 578        qdict_put(output_opts, "server.port",
 579                  qstring_from_str(port ?: stringify(22)));
 580    }
 581
 582    return true;
 583}
 584
 585static InetSocketAddress *ssh_config(QDict *options, Error **errp)
 586{
 587    InetSocketAddress *inet = NULL;
 588    QDict *addr = NULL;
 589    QObject *crumpled_addr = NULL;
 590    Visitor *iv = NULL;
 591    Error *local_error = NULL;
 592
 593    qdict_extract_subqdict(options, &addr, "server.");
 594    if (!qdict_size(addr)) {
 595        error_setg(errp, "SSH server address missing");
 596        goto out;
 597    }
 598
 599    crumpled_addr = qdict_crumple(addr, errp);
 600    if (!crumpled_addr) {
 601        goto out;
 602    }
 603
 604    iv = qobject_input_visitor_new(crumpled_addr, true);
 605    visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
 606    if (local_error) {
 607        error_propagate(errp, local_error);
 608        goto out;
 609    }
 610
 611out:
 612    QDECREF(addr);
 613    qobject_decref(crumpled_addr);
 614    visit_free(iv);
 615    return inet;
 616}
 617
 618static int connect_to_ssh(BDRVSSHState *s, QDict *options,
 619                          int ssh_flags, int creat_mode, Error **errp)
 620{
 621    int r, ret;
 622    QemuOpts *opts = NULL;
 623    Error *local_err = NULL;
 624    const char *user, *path, *host_key_check;
 625    long port = 0;
 626
 627    opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort);
 628    qemu_opts_absorb_qdict(opts, options, &local_err);
 629    if (local_err) {
 630        ret = -EINVAL;
 631        error_propagate(errp, local_err);
 632        goto err;
 633    }
 634
 635    if (!ssh_process_legacy_socket_options(options, opts, errp)) {
 636        ret = -EINVAL;
 637        goto err;
 638    }
 639
 640    path = qemu_opt_get(opts, "path");
 641    if (!path) {
 642        ret = -EINVAL;
 643        error_setg(errp, "No path was specified");
 644        goto err;
 645    }
 646
 647    user = qemu_opt_get(opts, "user");
 648    if (!user) {
 649        user = g_get_user_name();
 650        if (!user) {
 651            error_setg_errno(errp, errno, "Can't get user name");
 652            ret = -errno;
 653            goto err;
 654        }
 655    }
 656
 657    host_key_check = qemu_opt_get(opts, "host_key_check");
 658    if (!host_key_check) {
 659        host_key_check = "yes";
 660    }
 661
 662    /* Pop the config into our state object, Exit if invalid */
 663    s->inet = ssh_config(options, errp);
 664    if (!s->inet) {
 665        ret = -EINVAL;
 666        goto err;
 667    }
 668
 669    if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
 670        error_setg(errp, "Use only numeric port value");
 671        ret = -EINVAL;
 672        goto err;
 673    }
 674
 675    /* Open the socket and connect. */
 676    s->sock = inet_connect_saddr(s->inet, errp, NULL, NULL);
 677    if (s->sock < 0) {
 678        ret = -EIO;
 679        goto err;
 680    }
 681
 682    /* Create SSH session. */
 683    s->session = libssh2_session_init();
 684    if (!s->session) {
 685        ret = -EINVAL;
 686        session_error_setg(errp, s, "failed to initialize libssh2 session");
 687        goto err;
 688    }
 689
 690#if TRACE_LIBSSH2 != 0
 691    libssh2_trace(s->session, TRACE_LIBSSH2);
 692#endif
 693
 694    r = libssh2_session_handshake(s->session, s->sock);
 695    if (r != 0) {
 696        ret = -EINVAL;
 697        session_error_setg(errp, s, "failed to establish SSH session");
 698        goto err;
 699    }
 700
 701    /* Check the remote host's key against known_hosts. */
 702    ret = check_host_key(s, s->inet->host, port, host_key_check,
 703                         errp);
 704    if (ret < 0) {
 705        goto err;
 706    }
 707
 708    /* Authenticate. */
 709    ret = authenticate(s, user, errp);
 710    if (ret < 0) {
 711        goto err;
 712    }
 713
 714    /* Start SFTP. */
 715    s->sftp = libssh2_sftp_init(s->session);
 716    if (!s->sftp) {
 717        session_error_setg(errp, s, "failed to initialize sftp handle");
 718        ret = -EINVAL;
 719        goto err;
 720    }
 721
 722    /* Open the remote file. */
 723    DPRINTF("opening file %s flags=0x%x creat_mode=0%o",
 724            path, ssh_flags, creat_mode);
 725    s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode);
 726    if (!s->sftp_handle) {
 727        session_error_setg(errp, s, "failed to open remote file '%s'", path);
 728        ret = -EINVAL;
 729        goto err;
 730    }
 731
 732    qemu_opts_del(opts);
 733
 734    r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
 735    if (r < 0) {
 736        sftp_error_setg(errp, s, "failed to read file attributes");
 737        return -EINVAL;
 738    }
 739
 740    return 0;
 741
 742 err:
 743    if (s->sftp_handle) {
 744        libssh2_sftp_close(s->sftp_handle);
 745    }
 746    s->sftp_handle = NULL;
 747    if (s->sftp) {
 748        libssh2_sftp_shutdown(s->sftp);
 749    }
 750    s->sftp = NULL;
 751    if (s->session) {
 752        libssh2_session_disconnect(s->session,
 753                                   "from qemu ssh client: "
 754                                   "error opening connection");
 755        libssh2_session_free(s->session);
 756    }
 757    s->session = NULL;
 758
 759    qemu_opts_del(opts);
 760
 761    return ret;
 762}
 763
 764static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
 765                         Error **errp)
 766{
 767    BDRVSSHState *s = bs->opaque;
 768    int ret;
 769    int ssh_flags;
 770
 771    ssh_state_init(s);
 772
 773    ssh_flags = LIBSSH2_FXF_READ;
 774    if (bdrv_flags & BDRV_O_RDWR) {
 775        ssh_flags |= LIBSSH2_FXF_WRITE;
 776    }
 777
 778    /* Start up SSH. */
 779    ret = connect_to_ssh(s, options, ssh_flags, 0, errp);
 780    if (ret < 0) {
 781        goto err;
 782    }
 783
 784    /* Go non-blocking. */
 785    libssh2_session_set_blocking(s->session, 0);
 786
 787    return 0;
 788
 789 err:
 790    if (s->sock >= 0) {
 791        close(s->sock);
 792    }
 793    s->sock = -1;
 794
 795    return ret;
 796}
 797
 798static QemuOptsList ssh_create_opts = {
 799    .name = "ssh-create-opts",
 800    .head = QTAILQ_HEAD_INITIALIZER(ssh_create_opts.head),
 801    .desc = {
 802        {
 803            .name = BLOCK_OPT_SIZE,
 804            .type = QEMU_OPT_SIZE,
 805            .help = "Virtual disk size"
 806        },
 807        { /* end of list */ }
 808    }
 809};
 810
 811static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
 812{
 813    int r, ret;
 814    int64_t total_size = 0;
 815    QDict *uri_options = NULL;
 816    BDRVSSHState s;
 817    ssize_t r2;
 818    char c[1] = { '\0' };
 819
 820    ssh_state_init(&s);
 821
 822    /* Get desired file size. */
 823    total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
 824                          BDRV_SECTOR_SIZE);
 825    DPRINTF("total_size=%" PRIi64, total_size);
 826
 827    uri_options = qdict_new();
 828    r = parse_uri(filename, uri_options, errp);
 829    if (r < 0) {
 830        ret = r;
 831        goto out;
 832    }
 833
 834    r = connect_to_ssh(&s, uri_options,
 835                       LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
 836                       LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
 837                       0644, errp);
 838    if (r < 0) {
 839        ret = r;
 840        goto out;
 841    }
 842
 843    if (total_size > 0) {
 844        libssh2_sftp_seek64(s.sftp_handle, total_size-1);
 845        r2 = libssh2_sftp_write(s.sftp_handle, c, 1);
 846        if (r2 < 0) {
 847            sftp_error_setg(errp, &s, "truncate failed");
 848            ret = -EINVAL;
 849            goto out;
 850        }
 851        s.attrs.filesize = total_size;
 852    }
 853
 854    ret = 0;
 855
 856 out:
 857    ssh_state_free(&s);
 858    if (uri_options != NULL) {
 859        QDECREF(uri_options);
 860    }
 861    return ret;
 862}
 863
 864static void ssh_close(BlockDriverState *bs)
 865{
 866    BDRVSSHState *s = bs->opaque;
 867
 868    ssh_state_free(s);
 869}
 870
 871static int ssh_has_zero_init(BlockDriverState *bs)
 872{
 873    BDRVSSHState *s = bs->opaque;
 874    /* Assume false, unless we can positively prove it's true. */
 875    int has_zero_init = 0;
 876
 877    if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
 878        if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) {
 879            has_zero_init = 1;
 880        }
 881    }
 882
 883    return has_zero_init;
 884}
 885
 886static void restart_coroutine(void *opaque)
 887{
 888    Coroutine *co = opaque;
 889
 890    DPRINTF("co=%p", co);
 891
 892    qemu_coroutine_enter(co);
 893}
 894
 895static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
 896{
 897    int r;
 898    IOHandler *rd_handler = NULL, *wr_handler = NULL;
 899    Coroutine *co = qemu_coroutine_self();
 900
 901    r = libssh2_session_block_directions(s->session);
 902
 903    if (r & LIBSSH2_SESSION_BLOCK_INBOUND) {
 904        rd_handler = restart_coroutine;
 905    }
 906    if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
 907        wr_handler = restart_coroutine;
 908    }
 909
 910    DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock,
 911            rd_handler, wr_handler);
 912
 913    aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
 914                       false, rd_handler, wr_handler, co);
 915}
 916
 917static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
 918                                          BlockDriverState *bs)
 919{
 920    DPRINTF("s->sock=%d", s->sock);
 921    aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
 922                       false, NULL, NULL, NULL);
 923}
 924
 925/* A non-blocking call returned EAGAIN, so yield, ensuring the
 926 * handlers are set up so that we'll be rescheduled when there is an
 927 * interesting event on the socket.
 928 */
 929static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
 930{
 931    set_fd_handler(s, bs);
 932    qemu_coroutine_yield();
 933    clear_fd_handler(s, bs);
 934}
 935
 936/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
 937 * in the remote file.  Notice that it just updates a field in the
 938 * sftp_handle structure, so there is no network traffic and it cannot
 939 * fail.
 940 *
 941 * However, `libssh2_sftp_seek64' does have a catastrophic effect on
 942 * performance since it causes the handle to throw away all in-flight
 943 * reads and buffered readahead data.  Therefore this function tries
 944 * to be intelligent about when to call the underlying libssh2 function.
 945 */
 946#define SSH_SEEK_WRITE 0
 947#define SSH_SEEK_READ  1
 948#define SSH_SEEK_FORCE 2
 949
 950static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
 951{
 952    bool op_read = (flags & SSH_SEEK_READ) != 0;
 953    bool force = (flags & SSH_SEEK_FORCE) != 0;
 954
 955    if (force || op_read != s->offset_op_read || offset != s->offset) {
 956        DPRINTF("seeking to offset=%" PRIi64, offset);
 957        libssh2_sftp_seek64(s->sftp_handle, offset);
 958        s->offset = offset;
 959        s->offset_op_read = op_read;
 960    }
 961}
 962
 963static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
 964                                 int64_t offset, size_t size,
 965                                 QEMUIOVector *qiov)
 966{
 967    ssize_t r;
 968    size_t got;
 969    char *buf, *end_of_vec;
 970    struct iovec *i;
 971
 972    DPRINTF("offset=%" PRIi64 " size=%zu", offset, size);
 973
 974    ssh_seek(s, offset, SSH_SEEK_READ);
 975
 976    /* This keeps track of the current iovec element ('i'), where we
 977     * will write to next ('buf'), and the end of the current iovec
 978     * ('end_of_vec').
 979     */
 980    i = &qiov->iov[0];
 981    buf = i->iov_base;
 982    end_of_vec = i->iov_base + i->iov_len;
 983
 984    /* libssh2 has a hard-coded limit of 2000 bytes per request,
 985     * although it will also do readahead behind our backs.  Therefore
 986     * we may have to do repeated reads here until we have read 'size'
 987     * bytes.
 988     */
 989    for (got = 0; got < size; ) {
 990    again:
 991        DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf);
 992        r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf);
 993        DPRINTF("sftp_read returned %zd", r);
 994
 995        if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
 996            co_yield(s, bs);
 997            goto again;
 998        }
 999        if (r < 0) {
1000            sftp_error_report(s, "read failed");
1001            s->offset = -1;
1002            return -EIO;
1003        }
1004        if (r == 0) {
1005            /* EOF: Short read so pad the buffer with zeroes and return it. */
1006            qemu_iovec_memset(qiov, got, 0, size - got);
1007            return 0;
1008        }
1009
1010        got += r;
1011        buf += r;
1012        s->offset += r;
1013        if (buf >= end_of_vec && got < size) {
1014            i++;
1015            buf = i->iov_base;
1016            end_of_vec = i->iov_base + i->iov_len;
1017        }
1018    }
1019
1020    return 0;
1021}
1022
1023static coroutine_fn int ssh_co_readv(BlockDriverState *bs,
1024                                     int64_t sector_num,
1025                                     int nb_sectors, QEMUIOVector *qiov)
1026{
1027    BDRVSSHState *s = bs->opaque;
1028    int ret;
1029
1030    qemu_co_mutex_lock(&s->lock);
1031    ret = ssh_read(s, bs, sector_num * BDRV_SECTOR_SIZE,
1032                   nb_sectors * BDRV_SECTOR_SIZE, qiov);
1033    qemu_co_mutex_unlock(&s->lock);
1034
1035    return ret;
1036}
1037
1038static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
1039                     int64_t offset, size_t size,
1040                     QEMUIOVector *qiov)
1041{
1042    ssize_t r;
1043    size_t written;
1044    char *buf, *end_of_vec;
1045    struct iovec *i;
1046
1047    DPRINTF("offset=%" PRIi64 " size=%zu", offset, size);
1048
1049    ssh_seek(s, offset, SSH_SEEK_WRITE);
1050
1051    /* This keeps track of the current iovec element ('i'), where we
1052     * will read from next ('buf'), and the end of the current iovec
1053     * ('end_of_vec').
1054     */
1055    i = &qiov->iov[0];
1056    buf = i->iov_base;
1057    end_of_vec = i->iov_base + i->iov_len;
1058
1059    for (written = 0; written < size; ) {
1060    again:
1061        DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf);
1062        r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf);
1063        DPRINTF("sftp_write returned %zd", r);
1064
1065        if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1066            co_yield(s, bs);
1067            goto again;
1068        }
1069        if (r < 0) {
1070            sftp_error_report(s, "write failed");
1071            s->offset = -1;
1072            return -EIO;
1073        }
1074        /* The libssh2 API is very unclear about this.  A comment in
1075         * the code says "nothing was acked, and no EAGAIN was
1076         * received!" which apparently means that no data got sent
1077         * out, and the underlying channel didn't return any EAGAIN
1078         * indication.  I think this is a bug in either libssh2 or
1079         * OpenSSH (server-side).  In any case, forcing a seek (to
1080         * discard libssh2 internal buffers), and then trying again
1081         * works for me.
1082         */
1083        if (r == 0) {
1084            ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
1085            co_yield(s, bs);
1086            goto again;
1087        }
1088
1089        written += r;
1090        buf += r;
1091        s->offset += r;
1092        if (buf >= end_of_vec && written < size) {
1093            i++;
1094            buf = i->iov_base;
1095            end_of_vec = i->iov_base + i->iov_len;
1096        }
1097
1098        if (offset + written > s->attrs.filesize)
1099            s->attrs.filesize = offset + written;
1100    }
1101
1102    return 0;
1103}
1104
1105static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
1106                                      int64_t sector_num,
1107                                      int nb_sectors, QEMUIOVector *qiov)
1108{
1109    BDRVSSHState *s = bs->opaque;
1110    int ret;
1111
1112    qemu_co_mutex_lock(&s->lock);
1113    ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
1114                    nb_sectors * BDRV_SECTOR_SIZE, qiov);
1115    qemu_co_mutex_unlock(&s->lock);
1116
1117    return ret;
1118}
1119
1120static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
1121{
1122    if (!s->unsafe_flush_warning) {
1123        error_report("warning: ssh server %s does not support fsync",
1124                     s->inet->host);
1125        if (what) {
1126            error_report("to support fsync, you need %s", what);
1127        }
1128        s->unsafe_flush_warning = true;
1129    }
1130}
1131
1132#ifdef HAS_LIBSSH2_SFTP_FSYNC
1133
1134static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
1135{
1136    int r;
1137
1138    DPRINTF("fsync");
1139 again:
1140    r = libssh2_sftp_fsync(s->sftp_handle);
1141    if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1142        co_yield(s, bs);
1143        goto again;
1144    }
1145    if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
1146        libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) {
1147        unsafe_flush_warning(s, "OpenSSH >= 6.3");
1148        return 0;
1149    }
1150    if (r < 0) {
1151        sftp_error_report(s, "fsync failed");
1152        return -EIO;
1153    }
1154
1155    return 0;
1156}
1157
1158static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
1159{
1160    BDRVSSHState *s = bs->opaque;
1161    int ret;
1162
1163    qemu_co_mutex_lock(&s->lock);
1164    ret = ssh_flush(s, bs);
1165    qemu_co_mutex_unlock(&s->lock);
1166
1167    return ret;
1168}
1169
1170#else /* !HAS_LIBSSH2_SFTP_FSYNC */
1171
1172static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
1173{
1174    BDRVSSHState *s = bs->opaque;
1175
1176    unsafe_flush_warning(s, "libssh2 >= 1.4.4");
1177    return 0;
1178}
1179
1180#endif /* !HAS_LIBSSH2_SFTP_FSYNC */
1181
1182static int64_t ssh_getlength(BlockDriverState *bs)
1183{
1184    BDRVSSHState *s = bs->opaque;
1185    int64_t length;
1186
1187    /* Note we cannot make a libssh2 call here. */
1188    length = (int64_t) s->attrs.filesize;
1189    DPRINTF("length=%" PRIi64, length);
1190
1191    return length;
1192}
1193
1194static BlockDriver bdrv_ssh = {
1195    .format_name                  = "ssh",
1196    .protocol_name                = "ssh",
1197    .instance_size                = sizeof(BDRVSSHState),
1198    .bdrv_parse_filename          = ssh_parse_filename,
1199    .bdrv_file_open               = ssh_file_open,
1200    .bdrv_create                  = ssh_create,
1201    .bdrv_close                   = ssh_close,
1202    .bdrv_has_zero_init           = ssh_has_zero_init,
1203    .bdrv_co_readv                = ssh_co_readv,
1204    .bdrv_co_writev               = ssh_co_writev,
1205    .bdrv_getlength               = ssh_getlength,
1206    .bdrv_co_flush_to_disk        = ssh_co_flush,
1207    .create_opts                  = &ssh_create_opts,
1208};
1209
1210static void bdrv_ssh_init(void)
1211{
1212    int r;
1213
1214    r = libssh2_init(0);
1215    if (r != 0) {
1216        fprintf(stderr, "libssh2 initialization failed, %d\n", r);
1217        exit(EXIT_FAILURE);
1218    }
1219
1220    bdrv_register(&bdrv_ssh);
1221}
1222
1223block_init(bdrv_ssh_init);
1224