static int check_host_key_hash(BDRVSSHState *s, const char *hash, int hash_type, size_t fingerprint_len, Error **errp) { const char *fingerprint; fingerprint = libssh2_hostkey_hash(s->session, hash_type); if (!fingerprint) { session_error_setg(errp, s, "failed to read remote host key"); return -EINVAL; } if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len, hash) != 0) { error_setg(errp, "remote host key does not match host_key_check '%s'", hash); return -EPERM; } return 0; }
static int connect_to_ssh(BDRVSSHState *s, QDict *options, int ssh_flags, int creat_mode, Error **errp) { int r, ret; const char *host, *user, *path, *host_key_check; int port; if (!qdict_haskey(options, "host")) { ret = -EINVAL; error_setg(errp, "No hostname was specified"); goto err; } host = qdict_get_str(options, "host"); if (qdict_haskey(options, "port")) { port = qdict_get_int(options, "port"); } else { port = 22; } if (!qdict_haskey(options, "path")) { ret = -EINVAL; error_setg(errp, "No path was specified"); goto err; } path = qdict_get_str(options, "path"); if (qdict_haskey(options, "user")) { user = qdict_get_str(options, "user"); } else { user = g_get_user_name(); if (!user) { error_setg_errno(errp, errno, "Can't get user name"); ret = -errno; goto err; } } if (qdict_haskey(options, "host_key_check")) { host_key_check = qdict_get_str(options, "host_key_check"); } else { host_key_check = "yes"; } /* Construct the host:port name for inet_connect. */ g_free(s->hostport); s->hostport = g_strdup_printf("%s:%d", host, port); /* Open the socket and connect. */ s->sock = inet_connect(s->hostport, errp); if (s->sock < 0) { ret = -errno; goto err; } /* Create SSH session. */ s->session = libssh2_session_init(); if (!s->session) { ret = -EINVAL; session_error_setg(errp, s, "failed to initialize libssh2 session"); goto err; } #if TRACE_LIBSSH2 != 0 libssh2_trace(s->session, TRACE_LIBSSH2); #endif r = libssh2_session_handshake(s->session, s->sock); if (r != 0) { ret = -EINVAL; session_error_setg(errp, s, "failed to establish SSH session"); goto err; } /* Check the remote host's key against known_hosts. */ ret = check_host_key(s, host, port, host_key_check, errp); if (ret < 0) { goto err; } /* Authenticate. */ ret = authenticate(s, user, errp); if (ret < 0) { goto err; } /* Start SFTP. */ s->sftp = libssh2_sftp_init(s->session); if (!s->sftp) { session_error_setg(errp, s, "failed to initialize sftp handle"); ret = -EINVAL; goto err; } /* Open the remote file. */ DPRINTF("opening file %s flags=0x%x creat_mode=0%o", path, ssh_flags, creat_mode); s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode); if (!s->sftp_handle) { session_error_setg(errp, s, "failed to open remote file '%s'", path); ret = -EINVAL; goto err; } r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs); if (r < 0) { sftp_error_setg(errp, s, "failed to read file attributes"); return -EINVAL; } /* Delete the options we've used; any not deleted will cause the * block layer to give an error about unused options. */ qdict_del(options, "host"); qdict_del(options, "port"); qdict_del(options, "user"); qdict_del(options, "path"); qdict_del(options, "host_key_check"); return 0; err: if (s->sftp_handle) { libssh2_sftp_close(s->sftp_handle); } s->sftp_handle = NULL; if (s->sftp) { libssh2_sftp_shutdown(s->sftp); } s->sftp = NULL; if (s->session) { libssh2_session_disconnect(s->session, "from qemu ssh client: " "error opening connection"); libssh2_session_free(s->session); } s->session = NULL; return ret; }
static int authenticate(BDRVSSHState *s, const char *user, Error **errp) { int r, ret; const char *userauthlist; LIBSSH2_AGENT *agent = NULL; struct libssh2_agent_publickey *identity; struct libssh2_agent_publickey *prev_identity = NULL; userauthlist = libssh2_userauth_list(s->session, user, strlen(user)); if (strstr(userauthlist, "publickey") == NULL) { ret = -EPERM; error_setg(errp, "remote server does not support \"publickey\" authentication"); goto out; } /* Connect to ssh-agent and try each identity in turn. */ agent = libssh2_agent_init(s->session); if (!agent) { ret = -EINVAL; session_error_setg(errp, s, "failed to initialize ssh-agent support"); goto out; } if (libssh2_agent_connect(agent)) { ret = -ECONNREFUSED; session_error_setg(errp, s, "failed to connect to ssh-agent"); goto out; } if (libssh2_agent_list_identities(agent)) { ret = -EINVAL; session_error_setg(errp, s, "failed requesting identities from ssh-agent"); goto out; } for(;;) { r = libssh2_agent_get_identity(agent, &identity, prev_identity); if (r == 1) { /* end of list */ break; } if (r < 0) { ret = -EINVAL; session_error_setg(errp, s, "failed to obtain identity from ssh-agent"); goto out; } r = libssh2_agent_userauth(agent, user, identity); if (r == 0) { /* Authenticated! */ ret = 0; goto out; } /* Failed to authenticate with this identity, try the next one. */ prev_identity = identity; } ret = -EPERM; error_setg(errp, "failed to authenticate using publickey authentication " "and the identities held by your ssh-agent"); out: if (agent != NULL) { /* Note: libssh2 implementation implicitly calls * libssh2_agent_disconnect if necessary. */ libssh2_agent_free(agent); } return ret; }
static int check_host_key_knownhosts(BDRVSSHState *s, const char *host, int port, Error **errp) { const char *home; char *knh_file = NULL; LIBSSH2_KNOWNHOSTS *knh = NULL; struct libssh2_knownhost *found; int ret, r; const char *hostkey; size_t len; int type; hostkey = libssh2_session_hostkey(s->session, &len, &type); if (!hostkey) { ret = -EINVAL; session_error_setg(errp, s, "failed to read remote host key"); goto out; } knh = libssh2_knownhost_init(s->session); if (!knh) { ret = -EINVAL; session_error_setg(errp, s, "failed to initialize known hosts support"); goto out; } home = getenv("HOME"); if (home) { knh_file = g_strdup_printf("%s/.ssh/known_hosts", home); } else { knh_file = g_strdup_printf("/root/.ssh/known_hosts"); } /* Read all known hosts from OpenSSH-style known_hosts file. */ libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH); r = libssh2_knownhost_checkp(knh, host, port, hostkey, len, LIBSSH2_KNOWNHOST_TYPE_PLAIN| LIBSSH2_KNOWNHOST_KEYENC_RAW, &found); switch (r) { case LIBSSH2_KNOWNHOST_CHECK_MATCH: /* OK */ DPRINTF("host key OK: %s", found->key); break; case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: ret = -EINVAL; session_error_setg(errp, s, "host key does not match the one in known_hosts" " (found key %s)", found->key); goto out; case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: ret = -EINVAL; session_error_setg(errp, s, "no host key was found in known_hosts"); goto out; case LIBSSH2_KNOWNHOST_CHECK_FAILURE: ret = -EINVAL; session_error_setg(errp, s, "failure matching the host key with known_hosts"); goto out; default: ret = -EINVAL; session_error_setg(errp, s, "unknown error matching the host key" " with known_hosts (%d)", r); goto out; } /* known_hosts checking successful. */ ret = 0; out: if (knh != NULL) { libssh2_knownhost_free(knh); } g_free(knh_file); return ret; }
static int connect_to_ssh(BDRVSSHState *s, QDict *options, int ssh_flags, int creat_mode, Error **errp) { int r, ret; QemuOpts *opts = NULL; Error *local_err = NULL; const char *user, *path, *host_key_check; long port = 0; opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); goto err; } if (!ssh_process_legacy_socket_options(options, opts, errp)) { ret = -EINVAL; goto err; } path = qemu_opt_get(opts, "path"); if (!path) { ret = -EINVAL; error_setg(errp, "No path was specified"); goto err; } user = qemu_opt_get(opts, "user"); if (!user) { user = g_get_user_name(); if (!user) { error_setg_errno(errp, errno, "Can't get user name"); ret = -errno; goto err; } } host_key_check = qemu_opt_get(opts, "host_key_check"); if (!host_key_check) { host_key_check = "yes"; } /* Pop the config into our state object, Exit if invalid */ s->inet = ssh_config(s, options, errp); if (!s->inet) { ret = -EINVAL; goto err; } if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) { error_setg(errp, "Use only numeric port value"); ret = -EINVAL; goto err; } /* Open the socket and connect. */ s->sock = inet_connect_saddr(s->inet, errp, NULL, NULL); if (s->sock < 0) { ret = -EIO; goto err; } /* Create SSH session. */ s->session = libssh2_session_init(); if (!s->session) { ret = -EINVAL; session_error_setg(errp, s, "failed to initialize libssh2 session"); goto err; } #if TRACE_LIBSSH2 != 0 libssh2_trace(s->session, TRACE_LIBSSH2); #endif r = libssh2_session_handshake(s->session, s->sock); if (r != 0) { ret = -EINVAL; session_error_setg(errp, s, "failed to establish SSH session"); goto err; } /* Check the remote host's key against known_hosts. */ ret = check_host_key(s, s->inet->host, port, host_key_check, errp); if (ret < 0) { goto err; } /* Authenticate. */ ret = authenticate(s, user, errp); if (ret < 0) { goto err; } /* Start SFTP. */ s->sftp = libssh2_sftp_init(s->session); if (!s->sftp) { session_error_setg(errp, s, "failed to initialize sftp handle"); ret = -EINVAL; goto err; } /* Open the remote file. */ DPRINTF("opening file %s flags=0x%x creat_mode=0%o", path, ssh_flags, creat_mode); s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode); if (!s->sftp_handle) { session_error_setg(errp, s, "failed to open remote file '%s'", path); ret = -EINVAL; goto err; } qemu_opts_del(opts); r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs); if (r < 0) { sftp_error_setg(errp, s, "failed to read file attributes"); return -EINVAL; } return 0; err: if (s->sftp_handle) { libssh2_sftp_close(s->sftp_handle); } s->sftp_handle = NULL; if (s->sftp) { libssh2_sftp_shutdown(s->sftp); } s->sftp = NULL; if (s->session) { libssh2_session_disconnect(s->session, "from qemu ssh client: " "error opening connection"); libssh2_session_free(s->session); } s->session = NULL; qemu_opts_del(opts); return ret; }