Loading block/ssh.c +56 −32 Original line number Diff line number Diff line Loading @@ -431,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash, } static int check_host_key(BDRVSSHState *s, const char *host, int port, const char *host_key_check, Error **errp) SshHostKeyCheck *hkc, Error **errp) { /* host_key_check=no */ if (strcmp(host_key_check, "no") == 0) { return 0; } SshHostKeyCheckMode mode; /* host_key_check=md5:xx:yy:zz:... */ if (strncmp(host_key_check, "md5:", 4) == 0) { return check_host_key_hash(s, &host_key_check[4], LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); if (hkc) { mode = hkc->mode; } else { mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS; } /* host_key_check=sha1:xx:yy:zz:... */ if (strncmp(host_key_check, "sha1:", 5) == 0) { return check_host_key_hash(s, &host_key_check[5], switch (mode) { case SSH_HOST_KEY_CHECK_MODE_NONE: return 0; case SSH_HOST_KEY_CHECK_MODE_HASH: if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) { return check_host_key_hash(s, hkc->u.hash.hash, LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) { return check_host_key_hash(s, hkc->u.hash.hash, LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp); } /* host_key_check=yes */ if (strcmp(host_key_check, "yes") == 0) { g_assert_not_reached(); break; case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS: return check_host_key_knownhosts(s, host, port, errp); default: g_assert_not_reached(); } error_setg(errp, "unknown host_key_check setting (%s)", host_key_check); return -EINVAL; } Loading Loading @@ -544,16 +548,22 @@ static QemuOptsList ssh_runtime_opts = { .type = QEMU_OPT_NUMBER, .help = "Port to connect to", }, { .name = "host_key_check", .type = QEMU_OPT_STRING, .help = "Defines how and what to check the host key against", }, { /* end of list */ } }, }; static bool ssh_process_legacy_socket_options(QDict *output_opts, static bool ssh_process_legacy_options(QDict *output_opts, QemuOpts *legacy_opts, Error **errp) { const char *host = qemu_opt_get(legacy_opts, "host"); const char *port = qemu_opt_get(legacy_opts, "port"); const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check"); if (!host && port) { error_setg(errp, "port may not be used without host"); Loading @@ -565,6 +575,28 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts, qdict_put_str(output_opts, "server.port", port ?: stringify(22)); } if (host_key_check) { if (strcmp(host_key_check, "no") == 0) { qdict_put_str(output_opts, "host-key-check.mode", "none"); } else if (strncmp(host_key_check, "md5:", 4) == 0) { qdict_put_str(output_opts, "host-key-check.mode", "hash"); qdict_put_str(output_opts, "host-key-check.type", "md5"); qdict_put_str(output_opts, "host-key-check.hash", &host_key_check[4]); } else if (strncmp(host_key_check, "sha1:", 5) == 0) { qdict_put_str(output_opts, "host-key-check.mode", "hash"); qdict_put_str(output_opts, "host-key-check.type", "sha1"); qdict_put_str(output_opts, "host-key-check.hash", &host_key_check[5]); } else if (strcmp(host_key_check, "yes") == 0) { qdict_put_str(output_opts, "host-key-check.mode", "known_hosts"); } else { error_setg(errp, "unknown host_key_check setting (%s)", host_key_check); return false; } } return true; } Loading @@ -585,7 +617,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) goto fail; } if (!ssh_process_legacy_socket_options(options, opts, errp)) { if (!ssh_process_legacy_options(options, opts, errp)) { goto fail; } Loading Loading @@ -629,16 +661,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, { BlockdevOptionsSsh *opts; int r, ret; const char *user, *host_key_check; const char *user; long port = 0; host_key_check = qdict_get_try_str(options, "host_key_check"); if (!host_key_check) { host_key_check = "yes"; } else { qdict_del(options, "host_key_check"); } opts = ssh_parse_options(options, errp); if (opts == NULL) { return -EINVAL; Loading Loading @@ -692,8 +717,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } /* Check the remote host's key against known_hosts. */ ret = check_host_key(s, s->inet->host, port, host_key_check, errp); ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp); if (ret < 0) { goto err; } Loading qapi/block-core.json +61 −2 Original line number Diff line number Diff line Loading @@ -2552,6 +2552,63 @@ '*cache-clean-interval': 'int', '*encrypt': 'BlockdevQcow2Encryption' } } ## # @SshHostKeyCheckMode: # # @none Don't check the host key at all # @hash Compare the host key with a given hash # @known_hosts Check the host key against the known_hosts file # # Since: 2.12 ## { 'enum': 'SshHostKeyCheckMode', 'data': [ 'none', 'hash', 'known_hosts' ] } ## # @SshHostKeyCheckHashType: # # @md5 The given hash is an md5 hash # @sha1 The given hash is an sha1 hash # # Since: 2.12 ## { 'enum': 'SshHostKeyCheckHashType', 'data': [ 'md5', 'sha1' ] } ## # @SshHostKeyHash: # # @type The hash algorithm used for the hash # @hash The expected hash value # # Since: 2.12 ## { 'struct': 'SshHostKeyHash', 'data': { 'type': 'SshHostKeyCheckHashType', 'hash': 'str' }} ## # @SshHostKeyDummy: # # For those union branches that don't need additional fields. # # Since: 2.12 ## { 'struct': 'SshHostKeyDummy', 'data': {} } ## # @SshHostKeyCheck: # # Since: 2.12 ## { 'union': 'SshHostKeyCheck', 'base': { 'mode': 'SshHostKeyCheckMode' }, 'discriminator': 'mode', 'data': { 'none': 'SshHostKeyDummy', 'hash': 'SshHostKeyHash', 'known_hosts': 'SshHostKeyDummy' } } ## # @BlockdevOptionsSsh: # Loading @@ -2562,14 +2619,16 @@ # @user: user as which to connect, defaults to current # local user name # # TODO: Expose the host_key_check option in QMP # @host-key-check: Defines how and what to check the host key against # (default: known_hosts) # # Since: 2.9 ## { 'struct': 'BlockdevOptionsSsh', 'data': { 'server': 'InetSocketAddress', 'path': 'str', '*user': 'str' } } '*user': 'str', '*host-key-check': 'SshHostKeyCheck' } } ## Loading Loading
block/ssh.c +56 −32 Original line number Diff line number Diff line Loading @@ -431,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash, } static int check_host_key(BDRVSSHState *s, const char *host, int port, const char *host_key_check, Error **errp) SshHostKeyCheck *hkc, Error **errp) { /* host_key_check=no */ if (strcmp(host_key_check, "no") == 0) { return 0; } SshHostKeyCheckMode mode; /* host_key_check=md5:xx:yy:zz:... */ if (strncmp(host_key_check, "md5:", 4) == 0) { return check_host_key_hash(s, &host_key_check[4], LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); if (hkc) { mode = hkc->mode; } else { mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS; } /* host_key_check=sha1:xx:yy:zz:... */ if (strncmp(host_key_check, "sha1:", 5) == 0) { return check_host_key_hash(s, &host_key_check[5], switch (mode) { case SSH_HOST_KEY_CHECK_MODE_NONE: return 0; case SSH_HOST_KEY_CHECK_MODE_HASH: if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) { return check_host_key_hash(s, hkc->u.hash.hash, LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) { return check_host_key_hash(s, hkc->u.hash.hash, LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp); } /* host_key_check=yes */ if (strcmp(host_key_check, "yes") == 0) { g_assert_not_reached(); break; case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS: return check_host_key_knownhosts(s, host, port, errp); default: g_assert_not_reached(); } error_setg(errp, "unknown host_key_check setting (%s)", host_key_check); return -EINVAL; } Loading Loading @@ -544,16 +548,22 @@ static QemuOptsList ssh_runtime_opts = { .type = QEMU_OPT_NUMBER, .help = "Port to connect to", }, { .name = "host_key_check", .type = QEMU_OPT_STRING, .help = "Defines how and what to check the host key against", }, { /* end of list */ } }, }; static bool ssh_process_legacy_socket_options(QDict *output_opts, static bool ssh_process_legacy_options(QDict *output_opts, QemuOpts *legacy_opts, Error **errp) { const char *host = qemu_opt_get(legacy_opts, "host"); const char *port = qemu_opt_get(legacy_opts, "port"); const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check"); if (!host && port) { error_setg(errp, "port may not be used without host"); Loading @@ -565,6 +575,28 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts, qdict_put_str(output_opts, "server.port", port ?: stringify(22)); } if (host_key_check) { if (strcmp(host_key_check, "no") == 0) { qdict_put_str(output_opts, "host-key-check.mode", "none"); } else if (strncmp(host_key_check, "md5:", 4) == 0) { qdict_put_str(output_opts, "host-key-check.mode", "hash"); qdict_put_str(output_opts, "host-key-check.type", "md5"); qdict_put_str(output_opts, "host-key-check.hash", &host_key_check[4]); } else if (strncmp(host_key_check, "sha1:", 5) == 0) { qdict_put_str(output_opts, "host-key-check.mode", "hash"); qdict_put_str(output_opts, "host-key-check.type", "sha1"); qdict_put_str(output_opts, "host-key-check.hash", &host_key_check[5]); } else if (strcmp(host_key_check, "yes") == 0) { qdict_put_str(output_opts, "host-key-check.mode", "known_hosts"); } else { error_setg(errp, "unknown host_key_check setting (%s)", host_key_check); return false; } } return true; } Loading @@ -585,7 +617,7 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) goto fail; } if (!ssh_process_legacy_socket_options(options, opts, errp)) { if (!ssh_process_legacy_options(options, opts, errp)) { goto fail; } Loading Loading @@ -629,16 +661,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, { BlockdevOptionsSsh *opts; int r, ret; const char *user, *host_key_check; const char *user; long port = 0; host_key_check = qdict_get_try_str(options, "host_key_check"); if (!host_key_check) { host_key_check = "yes"; } else { qdict_del(options, "host_key_check"); } opts = ssh_parse_options(options, errp); if (opts == NULL) { return -EINVAL; Loading Loading @@ -692,8 +717,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } /* Check the remote host's key against known_hosts. */ ret = check_host_key(s, s->inet->host, port, host_key_check, errp); ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp); if (ret < 0) { goto err; } Loading
qapi/block-core.json +61 −2 Original line number Diff line number Diff line Loading @@ -2552,6 +2552,63 @@ '*cache-clean-interval': 'int', '*encrypt': 'BlockdevQcow2Encryption' } } ## # @SshHostKeyCheckMode: # # @none Don't check the host key at all # @hash Compare the host key with a given hash # @known_hosts Check the host key against the known_hosts file # # Since: 2.12 ## { 'enum': 'SshHostKeyCheckMode', 'data': [ 'none', 'hash', 'known_hosts' ] } ## # @SshHostKeyCheckHashType: # # @md5 The given hash is an md5 hash # @sha1 The given hash is an sha1 hash # # Since: 2.12 ## { 'enum': 'SshHostKeyCheckHashType', 'data': [ 'md5', 'sha1' ] } ## # @SshHostKeyHash: # # @type The hash algorithm used for the hash # @hash The expected hash value # # Since: 2.12 ## { 'struct': 'SshHostKeyHash', 'data': { 'type': 'SshHostKeyCheckHashType', 'hash': 'str' }} ## # @SshHostKeyDummy: # # For those union branches that don't need additional fields. # # Since: 2.12 ## { 'struct': 'SshHostKeyDummy', 'data': {} } ## # @SshHostKeyCheck: # # Since: 2.12 ## { 'union': 'SshHostKeyCheck', 'base': { 'mode': 'SshHostKeyCheckMode' }, 'discriminator': 'mode', 'data': { 'none': 'SshHostKeyDummy', 'hash': 'SshHostKeyHash', 'known_hosts': 'SshHostKeyDummy' } } ## # @BlockdevOptionsSsh: # Loading @@ -2562,14 +2619,16 @@ # @user: user as which to connect, defaults to current # local user name # # TODO: Expose the host_key_check option in QMP # @host-key-check: Defines how and what to check the host key against # (default: known_hosts) # # Since: 2.9 ## { 'struct': 'BlockdevOptionsSsh', 'data': { 'server': 'InetSocketAddress', 'path': 'str', '*user': 'str' } } '*user': 'str', '*host-key-check': 'SshHostKeyCheck' } } ## Loading