diff options
Diffstat (limited to 'nbd/client.c')
-rw-r--r-- | nbd/client.c | 136 |
1 files changed, 135 insertions, 1 deletions
diff --git a/nbd/client.c b/nbd/client.c index 5e47ac7792..9e5b651082 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -83,10 +83,18 @@ static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp) error_setg(errp, "Unsupported option type %x", opt); break; + case NBD_REP_ERR_POLICY: + error_setg(errp, "Denied by server for option %x", opt); + break; + case NBD_REP_ERR_INVALID: error_setg(errp, "Invalid data length for option %x", opt); break; + case NBD_REP_ERR_TLS_REQD: + error_setg(errp, "TLS negotiation required before option %x", opt); + break; + default: error_setg(errp, "Unknown error code when asking for option %x", opt); break; @@ -242,17 +250,127 @@ static int nbd_receive_query_exports(QIOChannel *ioc, return 0; } +static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp) +{ + uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC); + uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS); + uint32_t length = 0; + uint32_t type; + QIOChannelTLS *tioc; + struct NBDTLSHandshakeData data = { 0 }; + + TRACE("Requesting TLS from server"); + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to send option magic"); + return NULL; + } + + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "Failed to send option number"); + return NULL; + } + + if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "Failed to send option length"); + return NULL; + } + + TRACE("Getting TLS reply from server1"); + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "failed to read option magic"); + return NULL; + } + magic = be64_to_cpu(magic); + if (magic != NBD_REP_MAGIC) { + error_setg(errp, "Unexpected option magic"); + return NULL; + } + TRACE("Getting TLS reply from server2"); + if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "failed to read option"); + return NULL; + } + opt = be32_to_cpu(opt); + if (opt != NBD_OPT_STARTTLS) { + error_setg(errp, "Unexpected option type %x expected %x", + opt, NBD_OPT_STARTTLS); + return NULL; + } + + TRACE("Getting TLS reply from server"); + if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) { + error_setg(errp, "failed to read option type"); + return NULL; + } + type = be32_to_cpu(type); + if (type != NBD_REP_ACK) { + error_setg(errp, "Server rejected request to start TLS %x", + type); + return NULL; + } + + TRACE("Getting TLS reply from server"); + if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "failed to read option length"); + return NULL; + } + length = be32_to_cpu(length); + if (length != 0) { + error_setg(errp, "Start TLS reponse was not zero %x", + length); + return NULL; + } + + TRACE("TLS request approved, setting up TLS"); + tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); + if (!tioc) { + return NULL; + } + data.loop = g_main_loop_new(g_main_context_default(), FALSE); + TRACE("Starting TLS hanshake"); + qio_channel_tls_handshake(tioc, + nbd_tls_handshake, + &data, + NULL); + + if (!data.complete) { + g_main_loop_run(data.loop); + } + g_main_loop_unref(data.loop); + if (data.error) { + error_propagate(errp, data.error); + object_unref(OBJECT(tioc)); + return NULL; + } + + return QIO_CHANNEL(tioc); +} + + int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, + QCryptoTLSCreds *tlscreds, const char *hostname, + QIOChannel **outioc, off_t *size, Error **errp) { char buf[256]; uint64_t magic, s; int rc; - TRACE("Receiving negotiation."); + TRACE("Receiving negotiation tlscreds=%p hostname=%s.", + tlscreds, hostname ? hostname : "<null>"); rc = -EINVAL; + if (outioc) { + *outioc = NULL; + } + if (tlscreds && !outioc) { + error_setg(errp, "Output I/O channel required for TLS"); + goto fail; + } + if (read_sync(ioc, buf, 8) != 8) { error_setg(errp, "Failed to read data"); goto fail; @@ -314,6 +432,18 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, error_setg(errp, "Failed to send clientflags field"); goto fail; } + if (tlscreds) { + if (fixedNewStyle) { + *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); + if (!*outioc) { + goto fail; + } + ioc = *outioc; + } else { + error_setg(errp, "Server does not support STARTTLS"); + goto fail; + } + } if (!name) { TRACE("Using default NBD export name \"\""); name = ""; @@ -371,6 +501,10 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, error_setg(errp, "Server does not support export names"); goto fail; } + if (tlscreds) { + error_setg(errp, "Server does not support STARTTLS"); + goto fail; + } if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { error_setg(errp, "Failed to read export length"); |