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

nbd: enable use of TLS with NBD block driver



This modifies the NBD driver so that it is possible to request
use of TLS. This is done by providing the 'tls-creds' parameter
with the ID of a previously created QCryptoTLSCreds object.

For example

  $QEMU -object tls-creds-x509,id=tls0,endpoint=client,\
                dir=/home/berrange/security/qemutls \
        -drive driver=nbd,host=localhost,port=9000,tls-creds=tls0

The client will drop the connection if the NBD server does not
provide TLS.

Signed-off-by: default avatarDaniel P. Berrange <berrange@redhat.com>
Message-Id: <1455129674-17255-15-git-send-email-berrange@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent f95910fe
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -394,8 +394,12 @@ void nbd_client_close(BlockDriverState *bs)
    nbd_teardown_connection(bs);
}

int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,
                    const char *export, Error **errp)
int nbd_client_init(BlockDriverState *bs,
                    QIOChannelSocket *sioc,
                    const char *export,
                    QCryptoTLSCreds *tlscreds,
                    const char *hostname,
                    Error **errp)
{
    NbdClientSession *client = nbd_get_client_session(bs);
    int ret;
@@ -406,7 +410,7 @@ int nbd_client_init(BlockDriverState *bs, QIOChannelSocket *sioc,

    ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
                                &client->nbdflags,
                                NULL, NULL,
                                tlscreds, hostname,
                                &client->ioc,
                                &client->size, errp);
    if (ret < 0) {
+2 −0
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs);
int nbd_client_init(BlockDriverState *bs,
                    QIOChannelSocket *sock,
                    const char *export_name,
                    QCryptoTLSCreds *tlscreds,
                    const char *hostname,
                    Error **errp);
void nbd_client_close(BlockDriverState *bs);

+69 −9
Original line number Diff line number Diff line
@@ -258,36 +258,92 @@ static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
    return sioc;
}


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_CLIENT) {
        error_setg(errp,
                   "Expecting TLS credentials with a client endpoint");
        return NULL;
    }
    object_ref(obj);
    return creds;
}


static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
                    Error **errp)
{
    BDRVNBDState *s = bs->opaque;
    char *export = NULL;
    int result;
    QIOChannelSocket *sioc;
    QIOChannelSocket *sioc = NULL;
    SocketAddress *saddr;
    const char *tlscredsid;
    QCryptoTLSCreds *tlscreds = NULL;
    const char *hostname = NULL;
    int ret = -EINVAL;

    /* Pop the config into our state object. Exit if invalid. */
    saddr = nbd_config(s, options, &export, errp);
    if (!saddr) {
        return -EINVAL;
        goto error;
    }

    tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds"));
    if (tlscredsid) {
        qdict_del(options, "tls-creds");
        tlscreds = nbd_get_tls_creds(tlscredsid, errp);
        if (!tlscreds) {
            goto error;
        }

        if (saddr->type != SOCKET_ADDRESS_KIND_INET) {
            error_setg(errp, "TLS only supported over IP sockets");
            goto error;
        }
        hostname = saddr->u.inet->host;
    }

    /* establish TCP connection, return error if it fails
     * TODO: Configurable retry-until-timeout behaviour.
     */
    sioc = nbd_establish_connection(saddr, errp);
    qapi_free_SocketAddress(saddr);
    if (!sioc) {
        g_free(export);
        return -ECONNREFUSED;
        ret = -ECONNREFUSED;
        goto error;
    }

    /* NBD handshake */
    result = nbd_client_init(bs, sioc, export, errp);
    ret = nbd_client_init(bs, sioc, export,
                          tlscreds, hostname, errp);
 error:
    if (sioc) {
        object_unref(OBJECT(sioc));
    }
    if (tlscreds) {
        object_unref(OBJECT(tlscreds));
    }
    qapi_free_SocketAddress(saddr);
    g_free(export);
    return result;
    return ret;
}

static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
@@ -349,6 +405,7 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
    const char *host   = qdict_get_try_str(options, "host");
    const char *port   = qdict_get_try_str(options, "port");
    const char *export = qdict_get_try_str(options, "export");
    const char *tlscreds = qdict_get_try_str(options, "tls-creds");

    qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));

@@ -383,6 +440,9 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
    if (export) {
        qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export)));
    }
    if (tlscreds) {
        qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds)));
    }

    bs->full_open_options = opts;
}