Commit ddffee39 authored by Daniel P. Berrangé's avatar Daniel P. Berrangé Committed by Paolo Bonzini
Browse files

nbd: enable use of TLS with nbd-server-start command



This modifies the nbd-server-start QMP command so that it
is possible to request use of TLS. This is done by adding
a new optional parameter "tls-creds" which provides the ID
of a previously created QCryptoTLSCreds object instance.

TLS is only supported when using an IPv4/IPv6 socket listener.

Signed-off-by: default avatarDaniel P. Berrange <berrange@redhat.com>
Message-Id: <1455129674-17255-17-git-send-email-berrange@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 145614a1
Loading
Loading
Loading
Loading
+100 −22
Original line number Diff line number Diff line
@@ -20,42 +20,126 @@
#include "block/nbd.h"
#include "io/channel-socket.h"

static QIOChannelSocket *server_ioc;
static int server_watch = -1;
typedef struct NBDServerData {
    QIOChannelSocket *listen_ioc;
    int watch;
    QCryptoTLSCreds *tlscreds;
} NBDServerData;

static NBDServerData *nbd_server;


static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
                           gpointer opaque)
{
    QIOChannelSocket *cioc;

    if (!nbd_server) {
        return FALSE;
    }

    cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
                                     NULL);
    if (!cioc) {
        return TRUE;
    }

    nbd_client_new(NULL, cioc, NULL, NULL, nbd_client_put);
    nbd_client_new(NULL, cioc,
                   nbd_server->tlscreds, NULL,
                   nbd_client_put);
    object_unref(OBJECT(cioc));
    return TRUE;
}

void qmp_nbd_server_start(SocketAddress *addr, Error **errp)

static void nbd_server_free(NBDServerData *server)
{
    if (server_ioc) {
        error_setg(errp, "NBD server already running");
    if (!server) {
        return;
    }

    server_ioc = qio_channel_socket_new();
    if (qio_channel_socket_listen_sync(server_ioc, addr, errp) < 0) {
    if (server->watch != -1) {
        g_source_remove(server->watch);
    }
    object_unref(OBJECT(server->listen_ioc));
    if (server->tlscreds) {
        object_unref(OBJECT(server->tlscreds));
    }

    g_free(server);
}

static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
{
    Object *obj;
    QCryptoTLSCreds *creds;

    obj = object_resolve_path_component(
        object_get_objects_root(), id);
    if (!obj) {
        error_setg(errp, "No TLS credentials with id '%s'",
                   id);
        return NULL;
    }
    creds = (QCryptoTLSCreds *)
        object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
    if (!creds) {
        error_setg(errp, "Object with id '%s' is not TLS credentials",
                   id);
        return NULL;
    }

    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
        error_setg(errp,
                   "Expecting TLS credentials with a server endpoint");
        return NULL;
    }
    object_ref(obj);
    return creds;
}


void qmp_nbd_server_start(SocketAddress *addr,
                          bool has_tls_creds, const char *tls_creds,
                          Error **errp)
{
    if (nbd_server) {
        error_setg(errp, "NBD server already running");
        return;
    }

    server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc),
    nbd_server = g_new0(NBDServerData, 1);
    nbd_server->watch = -1;
    nbd_server->listen_ioc = qio_channel_socket_new();
    if (qio_channel_socket_listen_sync(
            nbd_server->listen_ioc, addr, errp) < 0) {
        goto error;
    }

    if (has_tls_creds) {
        nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
        if (!nbd_server->tlscreds) {
            goto error;
        }

        if (addr->type != SOCKET_ADDRESS_KIND_INET) {
            error_setg(errp, "TLS is only supported with IPv4/IPv6");
            goto error;
        }
    }

    nbd_server->watch = qio_channel_add_watch(
        QIO_CHANNEL(nbd_server->listen_ioc),
        G_IO_IN,
        nbd_accept,
        NULL,
        NULL);

    return;

 error:
    nbd_server_free(nbd_server);
    nbd_server = NULL;
}

void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
@@ -64,7 +148,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
    BlockBackend *blk;
    NBDExport *exp;

    if (!server_ioc) {
    if (!nbd_server) {
        error_setg(errp, "NBD server not running");
        return;
    }
@@ -110,12 +194,6 @@ void qmp_nbd_server_stop(Error **errp)
{
    nbd_export_close_all();

    if (server_watch != -1) {
        g_source_remove(server_watch);
        server_watch = -1;
    }
    if (server_ioc) {
        object_unref(OBJECT(server_ioc));
        server_ioc = NULL;
    }
    nbd_server_free(nbd_server);
    nbd_server = NULL;
}
+1 −1
Original line number Diff line number Diff line
@@ -1793,7 +1793,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
        goto exit;
    }

    qmp_nbd_server_start(addr, &local_err);
    qmp_nbd_server_start(addr, false, NULL, &local_err);
    qapi_free_SocketAddress(addr);
    if (local_err != NULL) {
        goto exit;
+3 −1
Original line number Diff line number Diff line
@@ -146,13 +146,15 @@
# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
#
# @addr: Address on which to listen.
# @tls-creds: (optional) ID of the TLS credentials object. Since 2.6
#
# Returns: error if the server is already running.
#
# Since: 1.3.0
##
{ 'command': 'nbd-server-start',
  'data': { 'addr': 'SocketAddress' } }
  'data': { 'addr': 'SocketAddress',
            '*tls-creds': 'str'} }

##
# @nbd-server-add:
+1 −1
Original line number Diff line number Diff line
@@ -3825,7 +3825,7 @@ EQMP

    {
        .name       = "nbd-server-start",
        .args_type  = "addr:q",
        .args_type  = "addr:q,tls-creds:s?",
        .mhandler.cmd_new = qmp_marshal_nbd_server_start,
    },
    {