summaryrefslogtreecommitdiff
path: root/nbd/server.c
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2016-02-10 18:41:11 +0000
committerPaolo Bonzini <pbonzini@redhat.com>2016-02-16 17:16:28 +0100
commitf95910fe6bbf64bb9b5cea7546a1778ba96ce782 (patch)
treeca32a89428ee209e5de8c7da9f7c52139a91ef6b /nbd/server.c
parent69b49502d8b7b582af79fac5bef7b7ccc2dc9c1e (diff)
downloadqemu-f95910fe6bbf64bb9b5cea7546a1778ba96ce782.tar.gz
nbd: implement TLS support in the protocol negotiation
This extends the NBD protocol handling code so that it is capable of negotiating TLS support during the connection setup. This involves requesting the STARTTLS protocol option before any other NBD options. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1455129674-17255-14-git-send-email-berrange@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'nbd/server.c')
-rw-r--r--nbd/server.c118
1 files changed, 111 insertions, 7 deletions
diff --git a/nbd/server.c b/nbd/server.c
index 9fee1d4fa4..d4225cdb55 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -76,6 +76,8 @@ struct NBDClient {
void (*close)(NBDClient *client);
NBDExport *exp;
+ QCryptoTLSCreds *tlscreds;
+ char *tlsaclname;
QIOChannelSocket *sioc; /* The underlying data channel */
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
@@ -192,6 +194,8 @@ static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt)
uint64_t magic;
uint32_t len;
+ TRACE("Reply opt=%x type=%x", type, opt);
+
magic = cpu_to_be64(NBD_REP_MAGIC);
if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
LOG("write failed (rep magic)");
@@ -310,6 +314,55 @@ fail:
return rc;
}
+
+static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
+ uint32_t length)
+{
+ QIOChannel *ioc;
+ QIOChannelTLS *tioc;
+ struct NBDTLSHandshakeData data = { 0 };
+
+ TRACE("Setting up TLS");
+ ioc = client->ioc;
+ if (length) {
+ if (nbd_negotiate_drop_sync(ioc, length) != length) {
+ return NULL;
+ }
+ nbd_negotiate_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS);
+ return NULL;
+ }
+
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_STARTTLS);
+
+ tioc = qio_channel_tls_new_server(ioc,
+ client->tlscreds,
+ client->tlsaclname,
+ NULL);
+ if (!tioc) {
+ return NULL;
+ }
+
+ TRACE("Starting TLS handshake");
+ data.loop = g_main_loop_new(g_main_context_default(), FALSE);
+ 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) {
+ object_unref(OBJECT(tioc));
+ error_free(data.error);
+ return NULL;
+ }
+
+ return QIO_CHANNEL(tioc);
+}
+
+
static int nbd_negotiate_options(NBDClient *client)
{
uint32_t flags;
@@ -377,7 +430,30 @@ static int nbd_negotiate_options(NBDClient *client)
length = be32_to_cpu(length);
TRACE("Checking option 0x%x", clientflags);
- if (fixedNewstyle) {
+ if (client->tlscreds &&
+ client->ioc == (QIOChannel *)client->sioc) {
+ QIOChannel *tioc;
+ if (!fixedNewstyle) {
+ TRACE("Unsupported option 0x%x", clientflags);
+ return -EINVAL;
+ }
+ switch (clientflags) {
+ case NBD_OPT_STARTTLS:
+ tioc = nbd_negotiate_handle_starttls(client, length);
+ if (!tioc) {
+ return -EIO;
+ }
+ object_unref(OBJECT(client->ioc));
+ client->ioc = QIO_CHANNEL(tioc);
+ break;
+
+ default:
+ TRACE("Option 0x%x not permitted before TLS", clientflags);
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_TLS_REQD,
+ clientflags);
+ return -EINVAL;
+ }
+ } else if (fixedNewstyle) {
switch (clientflags) {
case NBD_OPT_LIST:
ret = nbd_negotiate_handle_list(client, length);
@@ -392,6 +468,17 @@ static int nbd_negotiate_options(NBDClient *client)
case NBD_OPT_EXPORT_NAME:
return nbd_negotiate_handle_export_name(client, length);
+ case NBD_OPT_STARTTLS:
+ if (client->tlscreds) {
+ TRACE("TLS already enabled");
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_INVALID,
+ clientflags);
+ } else {
+ TRACE("TLS not configured");
+ nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_POLICY,
+ clientflags);
+ }
+ return -EINVAL;
default:
TRACE("Unsupported option 0x%x", clientflags);
nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP,
@@ -427,8 +514,9 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
int rc;
const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA);
+ bool oldStyle;
- /* Negotiation header without options:
+ /* Old style negotiation header without options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_CLIENT_MAGIC)
[16 .. 23] size
@@ -436,12 +524,11 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
[26 .. 27] export flags
[28 .. 151] reserved (0)
- Negotiation header with options, part 1:
+ New style negotiation header with options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_OPTS_MAGIC)
[16 .. 17] server flags (0)
-
- part 2 (after options are sent):
+ ....options sent....
[18 .. 25] size
[26 .. 27] export flags
[28 .. 151] reserved (0)
@@ -453,7 +540,9 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
TRACE("Beginning negotiation.");
memset(buf, 0, sizeof(buf));
memcpy(buf, "NBDMAGIC", 8);
- if (client->exp) {
+
+ oldStyle = client->exp != NULL && !client->tlscreds;
+ if (oldStyle) {
assert ((client->exp->nbdflags & ~65535) == 0);
stq_be_p(buf + 8, NBD_CLIENT_MAGIC);
stq_be_p(buf + 16, client->exp->size);
@@ -463,7 +552,11 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE);
}
- if (client->exp) {
+ if (oldStyle) {
+ if (client->tlscreds) {
+ TRACE("TLS cannot be enabled with oldstyle protocol");
+ goto fail;
+ }
if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) {
LOG("write failed");
goto fail;
@@ -602,6 +695,10 @@ void nbd_client_put(NBDClient *client)
nbd_unset_handlers(client);
object_unref(OBJECT(client->sioc));
object_unref(OBJECT(client->ioc));
+ if (client->tlscreds) {
+ object_unref(OBJECT(client->tlscreds));
+ }
+ g_free(client->tlsaclname);
if (client->exp) {
QTAILQ_REMOVE(&client->exp->clients, client, next);
nbd_export_put(client->exp);
@@ -1150,6 +1247,8 @@ out:
void nbd_client_new(NBDExport *exp,
QIOChannelSocket *sioc,
+ QCryptoTLSCreds *tlscreds,
+ const char *tlsaclname,
void (*close_fn)(NBDClient *))
{
NBDClient *client;
@@ -1158,6 +1257,11 @@ void nbd_client_new(NBDExport *exp,
client = g_malloc0(sizeof(NBDClient));
client->refcount = 1;
client->exp = exp;
+ client->tlscreds = tlscreds;
+ if (tlscreds) {
+ object_ref(OBJECT(client->tlscreds));
+ }
+ client->tlsaclname = g_strdup(tlsaclname);
client->sioc = sioc;
object_ref(OBJECT(client->sioc));
client->ioc = QIO_CHANNEL(sioc);