diff mbox

[v1,RFC,34/34] char: introduce support for TLS encrypted TCP chardev backend

Message ID 1429280557-8887-35-git-send-email-berrange@redhat.com
State New
Headers show

Commit Message

Daniel P. Berrangé April 17, 2015, 2:22 p.m. UTC
This integrates support for QIOChannelTLS object in the TCP
chardev backend. If the 'tls-cred=NAME' option is passed with
the '-chardev tcp' argument, then it will setup the chardev
such that the client is required to establish a TLS handshake
when connecting. The 'acl' option will further enable the
creation of a 'char.$ID.tlspeername' ACL which will be used
to validate the client x509 certificate, if provided.

A complete invokation to run QEMU as the server for a TLS
encrypted serial dev might be

  $ qemu-system-x86_64 \
      -nodefconfig -nodefaults -device sga -display none \
      -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
      -device isa-serial,chardev=s0 \
      -object qcrypto-tls-cred,id=tls0,credtype=x509,\
        endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off

To test with the gnutls-cli tool as the client:

  $ gnutls-cli --priority=NORMAL -p 9000 \
       --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
       127.0.0.1

If QEMU was told to use 'anon' credential type, then use the
priority string 'NOMAL:+ANON-DH' with gnutls-cli

Alternatively, if setting up a chardev to operate as a client,
then the TLS credentials registered must be for the client
endpoint. First a TLS server must be setup, which can be done
with the gnutls-serv tool

  $ gnutls-serv --priority=NORMAL -p 9000 \
       --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
       --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
       --x509keyfile=/home/berrange/security/qemutls/server-key.pem

Then QEMU can connect with

  $ qemu-system-x86_64 \
      -nodefconfig -nodefaults -device sga -display none \
      -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
      -device isa-serial,chardev=s0 \
      -object qcrypto-tls-cred,id=tls0,credtype=x509,\
        endpoint=client,dir=/home/berrange/security/qemutls

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qapi-schema.json |   2 +
 qemu-char.c      | 182 ++++++++++++++++++++++++++++++++++++++++++++++---------
 qemu-options.hx  |   9 ++-
 3 files changed, 161 insertions(+), 32 deletions(-)

Comments

Eric Blake April 17, 2015, 6:27 p.m. UTC | #1
On 04/17/2015 08:22 AM, Daniel P. Berrange wrote:
> This integrates support for QIOChannelTLS object in the TCP
> chardev backend. If the 'tls-cred=NAME' option is passed with
> the '-chardev tcp' argument, then it will setup the chardev
> such that the client is required to establish a TLS handshake
> when connecting. The 'acl' option will further enable the
> creation of a 'char.$ID.tlspeername' ACL which will be used
> to validate the client x509 certificate, if provided.
> 
> A complete invokation to run QEMU as the server for a TLS

s/invokation/invocation/

> encrypted serial dev might be
> 
>   $ qemu-system-x86_64 \
>       -nodefconfig -nodefaults -device sga -display none \
>       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
>       -device isa-serial,chardev=s0 \
>       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
>         endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off
> 
> To test with the gnutls-cli tool as the client:
> 
>   $ gnutls-cli --priority=NORMAL -p 9000 \
>        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
>        127.0.0.1
> 
> If QEMU was told to use 'anon' credential type, then use the
> priority string 'NOMAL:+ANON-DH' with gnutls-cli

s/NOMAL/NORMAL/

> 
> Alternatively, if setting up a chardev to operate as a client,
> then the TLS credentials registered must be for the client
> endpoint. First a TLS server must be setup, which can be done
> with the gnutls-serv tool
> 
>   $ gnutls-serv --priority=NORMAL -p 9000 \
>        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
>        --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
>        --x509keyfile=/home/berrange/security/qemutls/server-key.pem
> 
> Then QEMU can connect with
> 
>   $ qemu-system-x86_64 \
>       -nodefconfig -nodefaults -device sga -display none \
>       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
>       -device isa-serial,chardev=s0 \
>       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
>         endpoint=client,dir=/home/berrange/security/qemutls
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  qapi-schema.json |   2 +
>  qemu-char.c      | 182 ++++++++++++++++++++++++++++++++++++++++++++++---------
>  qemu-options.hx  |   9 ++-
>  3 files changed, 161 insertions(+), 32 deletions(-)
> 
> diff --git a/qapi-schema.json b/qapi-schema.json
> index ac9594d..062a455 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -2782,6 +2782,8 @@
>  # Since: 1.4
>  ##
>  { 'type': 'ChardevSocket', 'data': { 'addr'       : 'SocketAddress',
> +                                     '*tls-cred'  : 'str',
> +                                     '*acl'       : 'str',

Need to document these two fields, along with '(since 2.4)' designators.

> +++ b/qemu-options.hx
> @@ -2009,7 +2009,7 @@ ETEXI
>  DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
>      "-chardev null,id=id[,mux=on|off]\n"
>      "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
> -    "         [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n"
> +    "         [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off][,tls-cred=ID][,acl] (tcp)\n"
>      "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n"
>      "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
>      "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
> @@ -2082,7 +2082,7 @@ Options to each backend are described below.
>  A void device. This device will not emit any data, and will drop any data it
>  receives. The null backend does not take any options.
>  
> -@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
> +@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}][,tls-cred=@var{id}]

Everyone else in this line had space before [
Daniel P. Berrangé April 23, 2015, 12:32 p.m. UTC | #2
On Fri, Apr 17, 2015 at 12:27:06PM -0600, Eric Blake wrote:
> On 04/17/2015 08:22 AM, Daniel P. Berrange wrote:
> > This integrates support for QIOChannelTLS object in the TCP
> > chardev backend. If the 'tls-cred=NAME' option is passed with
> > the '-chardev tcp' argument, then it will setup the chardev
> > such that the client is required to establish a TLS handshake
> > when connecting. The 'acl' option will further enable the
> > creation of a 'char.$ID.tlspeername' ACL which will be used
> > to validate the client x509 certificate, if provided.
> > 
> > A complete invokation to run QEMU as the server for a TLS
> 
> s/invokation/invocation/
> 
> > encrypted serial dev might be
> > 
> >   $ qemu-system-x86_64 \
> >       -nodefconfig -nodefaults -device sga -display none \
> >       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
> >       -device isa-serial,chardev=s0 \
> >       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
> >         endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off
> > 
> > To test with the gnutls-cli tool as the client:
> > 
> >   $ gnutls-cli --priority=NORMAL -p 9000 \
> >        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
> >        127.0.0.1
> > 
> > If QEMU was told to use 'anon' credential type, then use the
> > priority string 'NOMAL:+ANON-DH' with gnutls-cli
> 
> s/NOMAL/NORMAL/
> 
> > 
> > Alternatively, if setting up a chardev to operate as a client,
> > then the TLS credentials registered must be for the client
> > endpoint. First a TLS server must be setup, which can be done
> > with the gnutls-serv tool
> > 
> >   $ gnutls-serv --priority=NORMAL -p 9000 \
> >        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
> >        --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
> >        --x509keyfile=/home/berrange/security/qemutls/server-key.pem
> > 
> > Then QEMU can connect with
> > 
> >   $ qemu-system-x86_64 \
> >       -nodefconfig -nodefaults -device sga -display none \
> >       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
> >       -device isa-serial,chardev=s0 \
> >       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
> >         endpoint=client,dir=/home/berrange/security/qemutls
> > 
> > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> > ---
> >  qapi-schema.json |   2 +
> >  qemu-char.c      | 182 ++++++++++++++++++++++++++++++++++++++++++++++---------
> >  qemu-options.hx  |   9 ++-
> >  3 files changed, 161 insertions(+), 32 deletions(-)
> > 
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index ac9594d..062a455 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -2782,6 +2782,8 @@
> >  # Since: 1.4
> >  ##
> >  { 'type': 'ChardevSocket', 'data': { 'addr'       : 'SocketAddress',
> > +                                     '*tls-cred'  : 'str',
> > +                                     '*acl'       : 'str',
> 
> Need to document these two fields, along with '(since 2.4)' designators.

Ah, ok forgot about that.

Regards,
Daniel
Kashyap Chamarthy May 4, 2015, 8:07 p.m. UTC | #3
On Fri, Apr 17, 2015 at 03:22:37PM +0100, Daniel P. Berrange wrote:
> This integrates support for QIOChannelTLS object in the TCP
> chardev backend. If the 'tls-cred=NAME' option is passed with
> the '-chardev tcp' argument, then it will setup the chardev
> such that the client is required to establish a TLS handshake
> when connecting. The 'acl' option will further enable the
> creation of a 'char.$ID.tlspeername' ACL which will be used
> to validate the client x509 certificate, if provided.
> 
> A complete invokation to run QEMU as the server for a TLS
> encrypted serial dev might be
> 
>   $ qemu-system-x86_64 \
>       -nodefconfig -nodefaults -device sga -display none \
>       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
>       -device isa-serial,chardev=s0 \
>       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
>         endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off
> 
> To test with the gnutls-cli tool as the client:
> 
>   $ gnutls-cli --priority=NORMAL -p 9000 \
>        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
>        127.0.0.1
> 
> If QEMU was told to use 'anon' credential type, then use the
> priority string 'NOMAL:+ANON-DH' with gnutls-cli
> 
> Alternatively, if setting up a chardev to operate as a client,
> then the TLS credentials registered must be for the client
> endpoint. First a TLS server must be setup, which can be done
> with the gnutls-serv tool
> 
>   $ gnutls-serv --priority=NORMAL -p 9000 \
>        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
>        --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
>        --x509keyfile=/home/berrange/security/qemutls/server-key.pem
> 
> Then QEMU can connect with
> 
>   $ qemu-system-x86_64 \
>       -nodefconfig -nodefaults -device sga -display none \
>       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
>       -device isa-serial,chardev=s0 \
>       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
>         endpoint=client,dir=/home/berrange/security/qemutls

I've applied your 'qemu-io-channel-7' branch locally, compiled QEMU and
began to play around.

    $ git describe
    v2.3.0-rc3-42-g5878696

When running QEMU either as server or as client, I notice this error
(further below are the details of how I tested):

    [. . .]
    qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
    invalid object type: qcrypto-tls-cred


Test with QEMU as client
------------------------

Setup PKI environment[1] , and run a GnuTLS server:

    $ gnutls-serv --priority=NORMAL -p 9000 \
        --x509cafile=/export/security/gnutls/ca-cert.pem \
        --x509certfile=/export/security/gnutls/server-cert.pem \
        --x509keyfile=/export/security/gnutls/server-key.pem
    Set static Diffie-Hellman parameters, consider --dhparams.
    Processed 1 CA certificate(s).
    HTTP Server listening on IPv4 0.0.0.0 port 9000...done
    HTTP Server listening on IPv6 :: port 9000...done

And, connect with QEMU:

    $ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
        -nodefconfig -nodefaults -device sga -display none \
        -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
        -device isa-serial,chardev=s0 \
        -object qcrypto-tls-cred,id=tls0,credtype=x509,\
        endpoint=client,dir=/export/security/gnutls
    qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
    invalid object type: qcrypto-tls-cred


Test with QEMU as server
------------------------

    $ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
        -nodefconfig -nodefaults -device sga -display none \
        -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0,server \
        -device isa-serial,chardev=s0 \
        -object qcrypto-tls-cred,id=tls0,credtype=x509,\
        endpoint=server,dir=/export/security/gnutls,verify-peer=off
    qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
    invalid object type: qcrypto-tls-cred


Am I missing something simple?


Additional notes
----------------

(a) I verified the QEMU CLI for -object is correct by looking at local the
    'qemu-options.hx' file:

    @item -object
    qcrypto-tls-cred,id=@var{id},credtype=@var{type},endpoint=@var{endpoint},
    dir=@var{/path/to/cred/dir},verify-peer=@var{on|off}

(b) Just to ensure that TLS server is setup correctly, I validated it via
    `gnutls-cli`:

    $ gnutls-cli --priority=NORMAL -p 9000 \
        --x509cafile=/export/security/gnutls/ca-cert.pem localhost
    [. . .]
    - Status: The certificate is trusted. 
    - Successfully sent 0 certificate(s) to server.
    - Compression: NULL
    - Options: safe renegotiation,
    - Handshake was completed
    [. . .]

(c) Exact CLI invocatoins of how I created the self-signed CA, server
    certificate including their outputs are noted here[1].

(d) When creating the server certificate request, I used the 'dnsName'
    attribute, and gave its value as "localhost".


[1] https://kashyapc.fedorapeople.org/gnutls-pki-setup.txt
Daniel P. Berrangé May 5, 2015, 1:49 p.m. UTC | #4
On Mon, May 04, 2015 at 10:07:15PM +0200, Kashyap Chamarthy wrote:
> On Fri, Apr 17, 2015 at 03:22:37PM +0100, Daniel P. Berrange wrote:
> > This integrates support for QIOChannelTLS object in the TCP
> > chardev backend. If the 'tls-cred=NAME' option is passed with
> > the '-chardev tcp' argument, then it will setup the chardev
> > such that the client is required to establish a TLS handshake
> > when connecting. The 'acl' option will further enable the
> > creation of a 'char.$ID.tlspeername' ACL which will be used
> > to validate the client x509 certificate, if provided.
> > 
> > A complete invokation to run QEMU as the server for a TLS
> > encrypted serial dev might be
> > 
> >   $ qemu-system-x86_64 \
> >       -nodefconfig -nodefaults -device sga -display none \
> >       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
> >       -device isa-serial,chardev=s0 \
> >       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
> >         endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off
> > 
> > To test with the gnutls-cli tool as the client:
> > 
> >   $ gnutls-cli --priority=NORMAL -p 9000 \
> >        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
> >        127.0.0.1
> > 
> > If QEMU was told to use 'anon' credential type, then use the
> > priority string 'NOMAL:+ANON-DH' with gnutls-cli
> > 
> > Alternatively, if setting up a chardev to operate as a client,
> > then the TLS credentials registered must be for the client
> > endpoint. First a TLS server must be setup, which can be done
> > with the gnutls-serv tool
> > 
> >   $ gnutls-serv --priority=NORMAL -p 9000 \
> >        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
> >        --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
> >        --x509keyfile=/home/berrange/security/qemutls/server-key.pem
> > 
> > Then QEMU can connect with
> > 
> >   $ qemu-system-x86_64 \
> >       -nodefconfig -nodefaults -device sga -display none \
> >       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
> >       -device isa-serial,chardev=s0 \
> >       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
> >         endpoint=client,dir=/home/berrange/security/qemutls
> 
> I've applied your 'qemu-io-channel-7' branch locally, compiled QEMU and
> began to play around.
> 
>     $ git describe
>     v2.3.0-rc3-42-g5878696
> 
> When running QEMU either as server or as client, I notice this error
> (further below are the details of how I tested):
> 
>     [. . .]
>     qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
>     invalid object type: qcrypto-tls-cred

Typo in my commit message - it should end in  '-creds' not '-cred' for
the object type.

Regards,
Daniel
Paolo Bonzini May 5, 2015, 1:53 p.m. UTC | #5
On 05/05/2015 15:49, Daniel P. Berrange wrote:
>> >     qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
>> >     invalid object type: qcrypto-tls-cred
> Typo in my commit message - it should end in  '-creds' not '-cred' for
> the object type.

FWIW, I think just "tls-creds" is probably a better name for the -object
option.  "qcrypto" is more of an internal detail.

Paolo
Daniel P. Berrangé May 5, 2015, 1:56 p.m. UTC | #6
On Tue, May 05, 2015 at 03:53:41PM +0200, Paolo Bonzini wrote:
> 
> 
> On 05/05/2015 15:49, Daniel P. Berrange wrote:
> >> >     qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
> >> >     invalid object type: qcrypto-tls-cred
> > Typo in my commit message - it should end in  '-creds' not '-cred' for
> > the object type.
> 
> FWIW, I think just "tls-creds" is probably a better name for the -object
> option.  "qcrypto" is more of an internal detail.

Ok, I'll drop the prefix from the name - I guess chance of a namespace
clash is pretty low anyway.

Regards,
Daniel
Kashyap Chamarthy May 5, 2015, 2:54 p.m. UTC | #7
On Tue, May 05, 2015 at 02:49:51PM +0100, Daniel P. Berrange wrote:
> On Mon, May 04, 2015 at 10:07:15PM +0200, Kashyap Chamarthy wrote:
> > On Fri, Apr 17, 2015 at 03:22:37PM +0100, Daniel P. Berrange wrote:
> > > This integrates support for QIOChannelTLS object in the TCP
> > > chardev backend. If the 'tls-cred=NAME' option is passed with
> > > the '-chardev tcp' argument, then it will setup the chardev
> > > such that the client is required to establish a TLS handshake
> > > when connecting. The 'acl' option will further enable the
> > > creation of a 'char.$ID.tlspeername' ACL which will be used
> > > to validate the client x509 certificate, if provided.
> > > 
> > > A complete invokation to run QEMU as the server for a TLS
> > > encrypted serial dev might be
> > > 
> > >   $ qemu-system-x86_64 \
> > >       -nodefconfig -nodefaults -device sga -display none \
> > >       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0,server \
> > >       -device isa-serial,chardev=s0 \
> > >       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
> > >         endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off
> > > 
> > > To test with the gnutls-cli tool as the client:
> > > 
> > >   $ gnutls-cli --priority=NORMAL -p 9000 \
> > >        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
> > >        127.0.0.1
> > > 
> > > If QEMU was told to use 'anon' credential type, then use the
> > > priority string 'NOMAL:+ANON-DH' with gnutls-cli
> > > 
> > > Alternatively, if setting up a chardev to operate as a client,
> > > then the TLS credentials registered must be for the client
> > > endpoint. First a TLS server must be setup, which can be done
> > > with the gnutls-serv tool
> > > 
> > >   $ gnutls-serv --priority=NORMAL -p 9000 \
> > >        --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
> > >        --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
> > >        --x509keyfile=/home/berrange/security/qemutls/server-key.pem
> > > 
> > > Then QEMU can connect with
> > > 
> > >   $ qemu-system-x86_64 \
> > >       -nodefconfig -nodefaults -device sga -display none \
> > >       -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-cred=tls0 \
> > >       -device isa-serial,chardev=s0 \
> > >       -object qcrypto-tls-cred,id=tls0,credtype=x509,\
> > >         endpoint=client,dir=/home/berrange/security/qemutls
> > 
> > I've applied your 'qemu-io-channel-7' branch locally, compiled QEMU and
> > began to play around.
> > 
> >     $ git describe
> >     v2.3.0-rc3-42-g5878696
> > 
> > When running QEMU either as server or as client, I notice this error
> > (further below are the details of how I tested):
> > 
> >     [. . .]
> >     qemu-system-x86_64: -object qcrypto-tls-cred,id=tls0,credtype=x509,:
> >     invalid object type: qcrypto-tls-cred
> 
> Typo in my commit message - it should end in  '-creds' not '-cred' for
> the object type.

Yep, that fixed it. I should have looked deeper -- your example in
include/crypto/tlscreds.h has the correct syntax and also includes a
QMP variant. Just to note, there seems to be three instances of this
typo in qemu-options.hx (found via `git grep qcrypto-tls-cred`).

While running QEMU as TLS server, the TLS handshake completes
successfully when connected via `gnutls-cli`.

However, when using QEMU as client to connect to an existing GnuTLS
server, I notice a segmentation fault:

  $ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
      -nodefconfig -nodefaults -device sga -display none \
      -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
      -device isa-serial,chardev=s0 \
      -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
  Segmentation fault (core dumped)
Kashyap Chamarthy May 6, 2015, 8:34 a.m. UTC | #8
On Tue, May 05, 2015 at 04:54:44PM +0200, Kashyap Chamarthy wrote:

[. . .]

> While running QEMU as TLS server, the TLS handshake completes
> successfully when connected via `gnutls-cli`.
> 
> However, when using QEMU as client to connect to an existing GnuTLS
> server, I notice a segmentation fault:
> 
>   $ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
>       -nodefconfig -nodefaults -device sga -display none \
>       -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
>       -device isa-serial,chardev=s0 \
>       -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
>   Segmentation fault (core dumped)

Some debugging with `gdb` below.

QEMU was built with:

    ./configure --target-list=x86_64-softmmu --enable-debug
    make -j4

Stack traces:

$ gdb /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64
[. . .]
(gdb) run  -nodefconfig -nodefaults -device sga -display none     -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0     -device isa-serial,chardev=s0     -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
Starting program: /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 -nodefconfig -nodefaults -device sga -display none     -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0     -device isa-serial,chardev=s0     -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
[. . .]
Program received signal SIGSEGV, Segmentation fault.
__strstr_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strstr-sse2-unaligned.S:40
40              movdqu  (%rdi), %xmm3
(gdb) thread apply all bt full

Thread 2 (Thread 0x7fffe4fcc700 (LWP 5393)):
#0  0x00007ffff6bce8fd in nanosleep () at ../sysdeps/unix/syscall-template.S:81
#1  0x00007ffff64f1de8 in g_usleep () at /lib64/libglib-2.0.so.0
#2  0x00005555559d32d7 in call_rcu_thread (opaque=0x0) at /home/kashyapc/tinker-space/qemu/util/rcu.c:228
        tries = 0
        n = 0
        node = 0x7ffff7fd19a0
#3  0x00007ffff6bc652a in start_thread (arg=0x7fffe4fcc700) at pthread_create.c:310
        __res = <optimized out>
        pd = 0x7fffe4fcc700
        now = <optimized out>
        unwind_buf = 
              {cancel_jmp_buf = {{jmp_buf = {140737035159296, 3180389637749088242, 140737488345857, 4096, 140737035159296, 140737035160000, -3180444589616128014, -3180404459381186574}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
        not_first_call = <optimized out>
        pagesize_m1 = <optimized out>
        sp = <optimized out>
        freesize = <optimized out>
#4  0x00007fffeea0979d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

---Type <return> to continue, or q <return> to quit---
Thread 1 (Thread 0x7ffff7f89bc0 (LWP 5389)):
#0  0x00007fffee9ae6dd in __strstr_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strstr-sse2-unaligned.S:40
#1  0x00007ffff1c6b370 in _gnutls_url_is_known () at /lib64/libgnutls.so.28
#2  0x00007ffff1c6b3d9 in gnutls_certificate_set_x509_key_file2 () at /lib64/libgnutls.so.28
#3  0x00005555559aba85 in qcrypto_tls_creds_load_x509 (creds=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:728
        cacert = 0x55555639a8c0 "/export/security/gnutls/ca-cert.pem"
        cacrl = 0x0
        cert = 0x0
        key = 0x0
        dhparams = 0x0
        ret = 1
        rv = -1
#4  0x00005555559abdb2 in qcrypto_tls_creds_load (creds=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:820
#5  0x00005555559abf30 in qcrypto_tls_creds_prop_set_loaded (obj=0x55555639ac60, value=true, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:888
        creds = 0x55555639ac60
        __func__ = "qcrypto_tls_creds_prop_set_loaded"
#6  0x00005555558cec1c in property_set_bool (obj=0x55555639ac60, v=0x55555639b4d0, opaque=0x55555639ad40, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8)
    at /home/kashyapc/tinker-space/qemu/qom/object.c:1600
        prop = 0x55555639ad40
        value = true
        local_err = 0x0
---Type <return> to continue, or q <return> to quit---
#7  0x00005555558cd485 in object_property_set (obj=0x55555639ac60, v=0x55555639b4d0, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/qom/object.c:901
        prop = 0x55555639ad60
#8  0x00005555558cfa47 in object_property_set_qobject (obj=0x55555639ac60, value=0x55555639b200, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8)
    at /home/kashyapc/tinker-space/qemu/qom/qom-qobject.c:24
        mi = 0x55555639b4d0
#9  0x00005555558cd6f4 in object_property_set_bool (obj=0x55555639ac60, value=true, name=0x555555a59695 "loaded", errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/qom/object.c:969
        qbool = 0x55555639b200
#10 0x00005555559ac2e5 in qcrypto_tls_creds_complete (uc=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:1018
#11 0x00005555558d0899 in user_creatable_complete (obj=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/qom/object_interfaces.c:17
        ucc = 0x5555563702f0
        uc = 0x55555639ac60
        __func__ = "user_creatable_complete"
#12 0x0000555555750201 in object_add (type=0x55555639a8f0 "qcrypto-tls-creds", id=0x55555639a850 "tls0", qdict=0x5555563997b0, v=0x5555563996a0, errp=0x7fffffffd920)
    at /home/kashyapc/tinker-space/qemu/qmp.c:659
        obj = 0x55555639ac60
        klass = 0x555556370050
        e = 0x0
        local_err = 0x0
#13 0x0000555555736a2d in object_create (opts=0x55555638a7e0, opaque=0x55555573684e <object_create_phase1>) at /home/kashyapc/tinker-space/qemu/vl.c:2644
        err = 0x0
        type = 0x55555639a8f0 "qcrypto-tls-creds"
---Type <return> to continue, or q <return> to quit---
        id = 0x55555639a850 "tls0"
        dummy = 0x55555639aaf0
        ov = 0x5555563996a0
        pdict = 0x5555563997b0
        type_predicate = 0x55555573684e <object_create_phase1>
#14 0x00005555559d08e0 in qemu_opts_foreach (list=0x555555e12ee0 <qemu_object_opts>, func=0x5555557368aa <object_create>, opaque=0x55555573684e <object_create_phase1>, abort_on_failure=0)
    at /home/kashyapc/tinker-space/qemu/util/qemu-option.c:1059
        loc = {kind = LOC_CMDLINE, num = 2, ptr = 0x7fffffffde10, prev = 0x555556315300 <std_loc>}
        opts = 0x55555638a7e0
        rc = 0
#15 0x000055555573a273 in main (argc=13, argv=0x7fffffffddb8, envp=0x7fffffffde28) at /home/kashyapc/tinker-space/qemu/vl.c:4039
        i = 21845
        snapshot = 0
        linux_boot = 3
        initrd_filename = 0xffff800000002441 <error: Cannot access memory at address 0xffff800000002441>
        kernel_filename = 0xffffffffffffffff <error: Cannot access memory at address 0xffffffffffffffff>
        kernel_cmdline = 0x555556345060 "\241x\244UUU"
        boot_order = 0x0
        boot_once = 0x0
        ds = 0x7fffffffdbbf
        cyls = 0
---Type <return> to continue, or q <return> to quit---
        heads = 0
        secs = 0
        translation = 0
        hda_opts = 0x0
        opts = 0x55555638aa50
        machine_opts = 0xffffffffffffffff
        icount_opts = 0x0
        olist = 0x0
        optind = 13
        optarg = 0x0
        loadvm = 0x0
        machine_class = 0x55555637ac70
        cpu_model = 0x0
        vga_model = 0x0
        qtest_chrdev = 0x0
        qtest_log = 0x0
        pid_file = 0x0
        incoming = 0x0
        show_vnc_port = 0
        defconfig = false
        userconfig = true
---Type <return> to continue, or q <return> to quit---
        log_mask = 0x0
        log_file = 0x0
        mem_trace = 
    {malloc = 0x5555557366c1 <malloc_and_trace>, realloc = 0x5555557366f6 <realloc_and_trace>, free = 0x55555573673a <free_and_trace>, calloc = 0x0, try_malloc = 0x0, try_realloc = 0x0}
        trace_events = 0x0
        trace_file = 0x0
        maxram_size = 134217728
        ram_slots = 0
        vmstate_dump_file = 0x0
        main_loop_err = 0x0
        err = 0x0
        __func__ = "main"
Daniel P. Berrangé May 6, 2015, 10:18 a.m. UTC | #9
On Wed, May 06, 2015 at 10:34:06AM +0200, Kashyap Chamarthy wrote:
> On Tue, May 05, 2015 at 04:54:44PM +0200, Kashyap Chamarthy wrote:
> 
> [. . .]
> 
> > While running QEMU as TLS server, the TLS handshake completes
> > successfully when connected via `gnutls-cli`.
> > 
> > However, when using QEMU as client to connect to an existing GnuTLS
> > server, I notice a segmentation fault:
> > 
> >   $ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
> >       -nodefconfig -nodefaults -device sga -display none \
> >       -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
> >       -device isa-serial,chardev=s0 \
> >       -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
> >   Segmentation fault (core dumped)
> 
> Some debugging with `gdb` below.
> 
> QEMU was built with:
> 
>     ./configure --target-list=x86_64-softmmu --enable-debug
>     make -j4
> 
> Stack traces:
> 
> $ gdb /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64

> #2  0x00007ffff1c6b3d9 in gnutls_certificate_set_x509_key_file2 () at /lib64/libgnutls.so.28
> #3  0x00005555559aba85 in qcrypto_tls_creds_load_x509 (creds=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:728
>         cacert = 0x55555639a8c0 "/export/security/gnutls/ca-cert.pem"
>         cacrl = 0x0
>         cert = 0x0
>         key = 0x0
>         dhparams = 0x0
>         ret = 1
>         rv = -1

Ah, with QEMU running in client mode, the client cert + key are optional. In this
case you've not provided them (cert & key are 0x0 ie NULL). We are then mistakenly
calling gnutls_certificate_set_x509_key_file2 - if I simply skip that I'll avoid
the crash. Thanks for testing this - I'll add a test case to validate this scenario
too

Regards,
Daniel
Kashyap Chamarthy May 6, 2015, 11:38 a.m. UTC | #10
On Wed, May 06, 2015 at 11:18:23AM +0100, Daniel P. Berrange wrote:
> On Wed, May 06, 2015 at 10:34:06AM +0200, Kashyap Chamarthy wrote:
> > On Tue, May 05, 2015 at 04:54:44PM +0200, Kashyap Chamarthy wrote:
> > 
> > [. . .]
> > 
> > > While running QEMU as TLS server, the TLS handshake completes
> > > successfully when connected via `gnutls-cli`.
> > > 
> > > However, when using QEMU as client to connect to an existing GnuTLS
> > > server, I notice a segmentation fault:
> > > 
> > >   $ /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64 \
> > >       -nodefconfig -nodefaults -device sga -display none \
> > >       -chardev socket,id=s0,host=localhost,port=9000,tls-cred=tls0 \
> > >       -device isa-serial,chardev=s0 \
> > >       -object qcrypto-tls-creds,id=tls0,credtype=x509,endpoint=client,dir=/export/security/gnutls
> > >   Segmentation fault (core dumped)
> > 
> > Some debugging with `gdb` below.
> > 
> > QEMU was built with:
> > 
> >     ./configure --target-list=x86_64-softmmu --enable-debug
> >     make -j4
> > 
> > Stack traces:
> > 
> > $ gdb /home/kashyapc/build/tls-qemu/x86_64-softmmu/qemu-system-x86_64
> 
> > #2  0x00007ffff1c6b3d9 in gnutls_certificate_set_x509_key_file2 () at /lib64/libgnutls.so.28
> > #3  0x00005555559aba85 in qcrypto_tls_creds_load_x509 (creds=0x55555639ac60, errp=0x7fffffffd8d8) at /home/kashyapc/tinker-space/qemu/crypto/tlscreds.c:728
> >         cacert = 0x55555639a8c0 "/export/security/gnutls/ca-cert.pem"
> >         cacrl = 0x0
> >         cert = 0x0
> >         key = 0x0
> >         dhparams = 0x0
> >         ret = 1
> >         rv = -1
> 
> Ah, with QEMU running in client mode, the client cert + key are
> optional. In this case you've not provided them (cert & key are 0x0 ie
> NULL).

Yep, I generated a client-key.pem, client-cert.pem and placed them in
the same gnutls directory where the ca* and server* files are located.
Indeed the TLS handshake completes succesfully (tested with the same
QEMU CLI as above placed in a script):

  $ ./chardev-backend-qemu-as-tls-client.sh 
  Handshake still running 2
  Handshake still running 2
  Handshake compelte session=0x7f8bdda6f4c0

On the GnuTLS server:

  [. . .]
  * Accepted connection from IPv4 127.0.0.1 port 51353 on Wed May  6 13:19:10 2015
  - Description: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-CBC)-(SHA1)
  [. . .]
  - Server has requested a certificate.
  - Certificate type: X.509
  - Got a certificate list of 1 certificates.
  [. . .]
  - Version: TLS1.2
  - Key Exchange: ECDHE-RSA
  - Server Signature: RSA-SHA256
  - Client Signature: RSA-SHA256
  - Compression: NULL
  - Options: safe renegotiation,
  - Channel binding 'tls-unique': 7f4ae1e0dc02dbad602a9c27

> We are then mistakenly calling gnutls_certificate_set_x509_key_file2 -
> if I simply skip that I'll avoid the crash. Thanks for testing this -
> I'll add a test case to validate this scenario too
diff mbox

Patch

diff --git a/qapi-schema.json b/qapi-schema.json
index ac9594d..062a455 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2782,6 +2782,8 @@ 
 # Since: 1.4
 ##
 { 'type': 'ChardevSocket', 'data': { 'addr'       : 'SocketAddress',
+                                     '*tls-cred'  : 'str',
+                                     '*acl'       : 'str',
                                      '*server'    : 'bool',
                                      '*wait'      : 'bool',
                                      '*nodelay'   : 'bool',
diff --git a/qemu-char.c b/qemu-char.c
index 85fbbaf..da6f188 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -33,6 +33,8 @@ 
 #include "qapi-visit.h"
 #include "io/channel-socket.h"
 #include "io/channel-file.h"
+#include "io/channel-tls.h"
+#include "qemu/acl.h"
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -2466,9 +2468,12 @@  static CharDriverState *qemu_chr_open_udp_fd(int fd)
 /* TCP Net console */
 
 typedef struct {
-    QIOChannel *ioc;
+    QIOChannel *ioc; /* Client I/O channel */
+    QIOChannelSocket *sioc; /* Client master channel */
     QIOChannelSocket *listen_ioc;
     guint listen_tag;
+    QCryptoTLSCreds *tls_creds;
+    char *acl;
     int connected;
     int max_size;
     int do_telnetopt;
@@ -2699,6 +2704,8 @@  static void tcp_chr_disconnect(CharDriverState *chr)
             QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr);
     }
     remove_fd_in_watch(chr);
+    object_unref(OBJECT(s->sioc));
+    s->sioc = NULL;
     object_unref(OBJECT(s->ioc));
     s->ioc = NULL;
     SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
@@ -2777,11 +2784,10 @@  static void tcp_chr_connect(void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
-    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(s->ioc);
 
     sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
-                    &sioc->localAddr, sioc->localAddrLen,
-                    &sioc->remoteAddr, sioc->remoteAddrLen,
+                    &s->sioc->localAddr, s->sioc->localAddrLen,
+                    &s->sioc->remoteAddr, s->sioc->remoteAddrLen,
                     s->is_listen, s->is_telnet);
 
     s->connected = 1;
@@ -2869,29 +2875,85 @@  static void tcp_chr_telnet_init(CharDriverState *chr)
         init);
 }
 
-static int tcp_chr_new_client(CharDriverState *chr, QIOChannel *ioc)
+
+static void tcp_chr_tls_handshake(QIOTask *task,
+                                  gpointer user_data)
+{
+    CharDriverState *chr = user_data;
+    TCPCharDriver *s = chr->opaque;
+
+    if (task->err) {
+        tcp_chr_disconnect(chr);
+    } else {
+        if (s->do_telnetopt) {
+            tcp_chr_telnet_init(chr);
+        } else {
+            tcp_chr_connect(chr);
+        }
+    }
+}
+
+
+static void tcp_chr_tls_init(CharDriverState *chr)
+{
+    TCPCharDriver *s = chr->opaque;
+    QIOChannelTLS *tioc;
+    Error *err = NULL;
+
+    if (s->is_listen) {
+        tioc = qio_channel_tls_new_server(
+            s->ioc, s->tls_creds,
+            s->acl,
+            &err);
+    } else {
+        tioc = qio_channel_tls_new_client(
+            s->ioc, s->tls_creds,
+            s->addr->inet->host,
+            &err);
+    }
+    if (tioc == NULL) {
+        error_free(err);
+        tcp_chr_disconnect(chr);
+    }
+    object_unref(OBJECT(s->ioc));
+    s->ioc = QIO_CHANNEL(tioc);
+
+    qio_channel_tls_handshake(tioc,
+                              tcp_chr_tls_handshake,
+                              chr,
+                              NULL);
+}
+
+
+static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 {
     TCPCharDriver *s = chr->opaque;
     if (s->ioc != NULL) {
 	return -1;
     }
 
-    s->ioc = ioc;
-    object_ref(OBJECT(ioc));
+    s->ioc = QIO_CHANNEL(sioc);
+    object_ref(OBJECT(s->ioc));
+    s->sioc = sioc;
+    object_ref(OBJECT(s->sioc));
 
     qio_channel_set_blocking(s->ioc, false);
     if (s->do_nodelay) {
-        qio_channel_socket_set_nodelay(QIO_CHANNEL_SOCKET(s->ioc), true);
+        qio_channel_socket_set_nodelay(s->sioc, true);
     }
     if (s->listen_tag) {
         g_source_remove(s->listen_tag);
         s->listen_tag = 0;
     }
 
-    if (s->do_telnetopt) {
-        tcp_chr_telnet_init(chr);
+    if (s->tls_creds) {
+        tcp_chr_tls_init(chr);
     } else {
-        tcp_chr_connect(chr);
+        if (s->do_telnetopt) {
+            tcp_chr_telnet_init(chr);
+        } else {
+            tcp_chr_connect(chr);
+        }
     }
 
     return 0;
@@ -2901,14 +2963,14 @@  static int tcp_chr_new_client(CharDriverState *chr, QIOChannel *ioc)
 static int tcp_chr_add_client(CharDriverState *chr, int fd)
 {
     int ret;
-    QIOChannel *ioc;
+    QIOChannelSocket *sioc;
 
-    ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, NULL));
-    if (!ioc) {
+    sioc = qio_channel_socket_new_fd(fd, NULL);
+    if (!sioc) {
         return -1;
     }
-    ret = tcp_chr_new_client(chr, ioc);
-    object_unref(OBJECT(ioc));
+    ret = tcp_chr_new_client(chr, sioc);
+    object_unref(OBJECT(sioc));
     return ret;
 }
 
@@ -2917,17 +2979,16 @@  static gboolean tcp_chr_accept(QIOChannel *channel,
                                void *opaque)
 {
     CharDriverState *chr = opaque;
-    QIOChannel *ioc;
+    QIOChannelSocket *sioc;
 
-    ioc = QIO_CHANNEL(
-        qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel), NULL));
-    if (!ioc) {
+    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel), NULL);
+    if (!sioc) {
         return TRUE;
     }
 
-    tcp_chr_new_client(chr, ioc);
+    tcp_chr_new_client(chr, sioc);
 
-    object_unref(OBJECT(ioc));
+    object_unref(OBJECT(sioc));
 
     return TRUE;
 }
@@ -2959,6 +3020,9 @@  static void tcp_chr_close(CharDriverState *chr)
         }
         g_free(s->read_msgfds);
     }
+    if (s->tls_creds) {
+        object_unref(OBJECT(s->tls_creds));
+    }
     if (s->write_msgfds_num) {
         g_free(s->write_msgfds);
     }
@@ -2979,13 +3043,13 @@  static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)
         s->listen_tag = qio_channel_add_watch(
             QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr);
     } else {
-        QIOChannel *ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, NULL));
-        if (!ioc) {
+        QIOChannelSocket *sioc = qio_channel_socket_new_fd(fd, NULL);
+        if (!sioc) {
             close(fd);
             return;
         }
-        tcp_chr_new_client(chr, ioc);
-        object_unref(OBJECT(ioc));
+        tcp_chr_new_client(chr, sioc);
+        object_unref(OBJECT(sioc));
     }
 }
 
@@ -3450,6 +3514,8 @@  static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     const char *path = qemu_opt_get(opts, "path");
     const char *host = qemu_opt_get(opts, "host");
     const char *port = qemu_opt_get(opts, "port");
+    const char *tls_cred = qemu_opt_get(opts, "tls-cred");
+    bool acl = qemu_opt_get_bool(opts, "acl", false);
     SocketAddress *addr;
 
     if (!path) {
@@ -3461,6 +3527,11 @@  static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
             error_setg(errp, "chardev: socket: no port given");
             return;
         }
+    } else {
+        if (tls_cred) {
+            error_setg(errp, "TLS can only be used over TCP socket");
+            return;
+        }
     }
 
     backend->socket = g_new0(ChardevSocket, 1);
@@ -3475,6 +3546,11 @@  static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     backend->socket->wait = is_waitconnect;
     backend->socket->has_reconnect = true;
     backend->socket->reconnect = reconnect;
+    backend->socket->tls_cred = g_strdup(tls_cred);
+    if (acl) {
+        backend->socket->acl = g_strdup_printf("chr.%s.tlspeername",
+                                               qemu_opts_id(opts));
+    }
 
     addr = g_new0(SocketAddress, 1);
     if (path) {
@@ -3494,6 +3570,7 @@  static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
     }
     backend->socket->addr = addr;
+    return;
 }
 
 static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
@@ -3877,6 +3954,9 @@  QemuOptsList qemu_chardev_opts = {
             .name = "telnet",
             .type = QEMU_OPT_BOOL,
         },{
+            .name = "tls-cred",
+            .type = QEMU_OPT_STRING,
+        },{
             .name = "width",
             .type = QEMU_OPT_NUMBER,
         },{
@@ -4061,6 +4141,41 @@  static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
     s->do_nodelay = do_nodelay;
+    if (sock->tls_cred) {
+        Object *container;
+        container = container_get(object_get_root(), "/objects");
+        /* XXX this type cast causes an abort if the type is wrong.
+         * This is bad. We should check and set an error */
+        s->tls_creds = QCRYPTO_TLS_CREDS(
+            object_resolve_path_component(container,
+                                          sock->tls_cred));
+        if (!s->tls_creds) {
+            error_setg(errp, "No TLS credentials with id '%s'",
+                       sock->tls_cred);
+            goto error;
+        }
+        object_ref(OBJECT(s->tls_creds));
+        if (is_listen) {
+            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+                error_setg(errp, "%s",
+                           "Expected TLS credentials for server endpoint");
+                goto error;
+            }
+        } else {
+            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+                error_setg(errp, "%s",
+                           "Expected TLS credentials for client endpoint");
+                goto error;
+            }
+        }
+
+        if (sock->acl) {
+            s->acl = g_strdup(sock->acl);
+
+            qemu_acl_init(s->acl);
+        }
+    }
+
     qapi_copy_SocketAddress(&s->addr, sock->addr);
 
     chr->opaque = s;
@@ -4090,10 +4205,7 @@  static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     if (s->reconnect_time) {
         socket_try_connect(chr);
     } else if (!qemu_chr_open_socket_fd(chr, errp)) {
-        g_free(s);
-        g_free(chr->filename);
-        g_free(chr);
-        return NULL;
+        goto error;
     }
 
     if (is_listen && is_waitconnect) {
@@ -4104,6 +4216,16 @@  static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     }
 
     return chr;
+
+ error:
+    if (s->tls_creds) {
+        object_unref(OBJECT(s->tls_creds));
+    }
+    g_free(s->acl);
+    g_free(s);
+    g_free(chr->filename);
+    g_free(chr);
+    return NULL;
 }
 
 static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
diff --git a/qemu-options.hx b/qemu-options.hx
index 44d9be2..0c764fc 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2009,7 +2009,7 @@  ETEXI
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off]\n"
     "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
-    "         [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n"
+    "         [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off][,tls-cred=ID][,acl] (tcp)\n"
     "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
@@ -2082,7 +2082,7 @@  Options to each backend are described below.
 A void device. This device will not emit any data, and will drop any data it
 receives. The null backend does not take any options.
 
-@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}][,tls-cred=@var{id}]
 
 Create a two-way stream socket, which can be either a TCP or a unix socket. A
 unix socket will be created if @option{path} is specified. Behaviour is
@@ -2100,6 +2100,11 @@  escape sequences.
 the remote end goes away.  qemu will delay this many seconds and then attempt
 to reconnect.  Zero disables reconnecting, and is the default.
 
+@option{tls-cred} requests enablement of the TLS protocol for encryption,
+and specifies the id of the TLS credentials to use for the handshake. The
+credentials must be previously created with the @option{-object qcrypto-tls-cred}
+argument.
+
 TCP and unix socket options are given below:
 
 @table @option