diff mbox

[v3,09/10] chardev: add support for ACLs for TLS clients

Message ID 1457636396-24983-9-git-send-email-berrange@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel P. Berrangé March 10, 2016, 6:59 p.m. UTC
Currently any client which can complete the TLS handshake
is able to use a chardev server. The server admin can turn
on the 'verify-peer' option for the x509 creds to require
the client to provide a x509 certificate. This means the
client will have to acquire a certificate from the CA before
they are permitted to use the chardev server. This is still
a fairly weak bar.

This adds a 'tls-acl=ACL-ID' option to the socket chardev
backend which takes the ID of a previously added 'QAuthZ'
object instance. This ACL will be used to validate the client's
x509 distinguished name. Clients failing the ACL will not be
permitted to use the chardev server.

For example to setup an ACL that only allows connection from
a client whose x509 certificate distinguished name contains
'CN=fred', you would use:

  $QEMU -object tls-creds-x509,id=tls0,dir=/home/berrange/qemutls,\
                endpoint=server,verify-peer=yes \
        -object authz-simple,id=acl0,policy=deny,\
                rules.0.match=*CN=fred,rules.0.policy=allow \
        -chardev socket,host=127.0.0.1,port=9000,server,\
	         tls-creds=tls0,tls-acl=acl0 \
        ...other qemud args...

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qapi-schema.json |  2 ++
 qemu-char.c      | 11 ++++++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

Comments

Eric Blake March 22, 2016, 9:26 p.m. UTC | #1
On 03/10/2016 11:59 AM, Daniel P. Berrange wrote:
> Currently any client which can complete the TLS handshake
> is able to use a chardev server. The server admin can turn
> on the 'verify-peer' option for the x509 creds to require
> the client to provide a x509 certificate. This means the
> client will have to acquire a certificate from the CA before
> they are permitted to use the chardev server. This is still
> a fairly weak bar.
> 
> This adds a 'tls-acl=ACL-ID' option to the socket chardev
> backend which takes the ID of a previously added 'QAuthZ'
> object instance. This ACL will be used to validate the client's
> x509 distinguished name. Clients failing the ACL will not be
> permitted to use the chardev server.
> 
> For example to setup an ACL that only allows connection from
> a client whose x509 certificate distinguished name contains
> 'CN=fred', you would use:
> 
>   $QEMU -object tls-creds-x509,id=tls0,dir=/home/berrange/qemutls,\
>                 endpoint=server,verify-peer=yes \
>         -object authz-simple,id=acl0,policy=deny,\
>                 rules.0.match=*CN=fred,rules.0.policy=allow \

Needs shell quoting for *, and also the same recurring comment about
whitespace for presentation not actually being in the command line.

Food for thought: should we enhance QemuOpts to skip all whitespace
after ',', since we _know_ that valid key names start with a letter
rather than a space?  Then, we could represent command lines as:

$QEMU -object 'name,
               param1=value,
               param2=value'

with the same semantics as:

$QEMU -object name,param1=value,param2=value

and without having to worry about backslash-newline-whitespace
formatting.  Obviously, such an enhancement would be a separate patch.

>         -chardev socket,host=127.0.0.1,port=9000,server,\
> 	         tls-creds=tls0,tls-acl=acl0 \
>         ...other qemud args...
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  qapi-schema.json |  2 ++
>  qemu-char.c      | 11 ++++++++++-
>  2 files changed, 12 insertions(+), 1 deletion(-)
> 

Code is fine; my only comments were on the commit message.
Reviewed-by: Eric Blake <eblake@redhat.com>
diff mbox

Patch

diff --git a/qapi-schema.json b/qapi-schema.json
index b6769de..a6a7205 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3202,6 +3202,7 @@ 
 # @addr: socket address to listen on (server=true)
 #        or connect to (server=false)
 # @tls-creds: #optional the ID of the TLS credentials object (since 2.6)
+# @tls-acl: #optional the ID of the QAuthZ authorization object (since 2.6)
 # @server: #optional create server socket (default: true)
 # @wait: #optional wait for incoming connection on server
 #        sockets (default: false).
@@ -3217,6 +3218,7 @@ 
 ##
 { 'struct': 'ChardevSocket', 'data': { 'addr'       : 'SocketAddress',
                                      '*tls-creds'  : 'str',
+                                     '*tls-acl'    : 'str',
                                      '*server'    : 'bool',
                                      '*wait'      : 'bool',
                                      '*nodelay'   : 'bool',
diff --git a/qemu-char.c b/qemu-char.c
index e0147f3..9533e7e 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2533,6 +2533,7 @@  typedef struct {
     QIOChannelSocket *listen_ioc;
     guint listen_tag;
     QCryptoTLSCreds *tls_creds;
+    char *tls_acl;
     int connected;
     int max_size;
     int do_telnetopt;
@@ -2963,7 +2964,7 @@  static void tcp_chr_tls_init(CharDriverState *chr)
     if (s->is_listen) {
         tioc = qio_channel_tls_new_server(
             s->ioc, s->tls_creds,
-            NULL, /* XXX Use an ACL */
+            s->tls_acl,
             &err);
     } else {
         tioc = qio_channel_tls_new_client(
@@ -3084,6 +3085,7 @@  static void tcp_chr_close(CharDriverState *chr)
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
+    g_free(s->tls_acl);
     if (s->write_msgfds_num) {
         g_free(s->write_msgfds);
     }
@@ -3623,6 +3625,7 @@  static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     const char *host = qemu_opt_get(opts, "host");
     const char *port = qemu_opt_get(opts, "port");
     const char *tls_creds = qemu_opt_get(opts, "tls-creds");
+    const char *tls_acl = qemu_opt_get(opts, "tls-acl");
     SocketAddress *addr;
     ChardevSocket *sock;
 
@@ -3656,6 +3659,7 @@  static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     sock->has_reconnect = true;
     sock->reconnect = reconnect;
     sock->tls_creds = g_strdup(tls_creds);
+    sock->tls_acl = g_strdup(tls_acl);
 
     addr = g_new0(SocketAddress, 1);
     if (path) {
@@ -4094,6 +4098,9 @@  QemuOptsList qemu_chardev_opts = {
             .name = "tls-creds",
             .type = QEMU_OPT_STRING,
         },{
+            .name = "tls-acl",
+            .type = QEMU_OPT_STRING,
+        },{
             .name = "width",
             .type = QEMU_OPT_NUMBER,
         },{
@@ -4341,6 +4348,7 @@  static CharDriverState *qmp_chardev_open_socket(const char *id,
             }
         }
     }
+    s->tls_acl = g_strdup(sock->tls_acl);
 
     qapi_copy_SocketAddress(&s->addr, sock->addr);
 
@@ -4386,6 +4394,7 @@  static CharDriverState *qmp_chardev_open_socket(const char *id,
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
+    g_free(s->tls_acl);
     g_free(s);
     qemu_chr_free_common(chr);
     return NULL;