qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

test-io-channel-tls.c (11696B)


      1 /*
      2  * QEMU I/O channel TLS test
      3  *
      4  * Copyright (C) 2015 Red Hat, Inc.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Lesser General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2.1 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Lesser General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Lesser General Public
     17  * License along with this library.  If not, see
     18  * <http://www.gnu.org/licenses/>.
     19  *
     20  * Author: Daniel P. Berrange <berrange@redhat.com>
     21  */
     22 
     23 
     24 #include "qemu/osdep.h"
     25 
     26 #include "crypto-tls-x509-helpers.h"
     27 #include "io/channel-tls.h"
     28 #include "io/channel-socket.h"
     29 #include "io-channel-helpers.h"
     30 #include "crypto/init.h"
     31 #include "crypto/tlscredsx509.h"
     32 #include "qapi/error.h"
     33 #include "qemu/module.h"
     34 #include "authz/list.h"
     35 #include "qom/object_interfaces.h"
     36 
     37 #define WORKDIR "tests/test-io-channel-tls-work/"
     38 #define KEYFILE WORKDIR "key-ctx.pem"
     39 
     40 struct QIOChannelTLSTestData {
     41     const char *servercacrt;
     42     const char *clientcacrt;
     43     const char *servercrt;
     44     const char *clientcrt;
     45     bool expectServerFail;
     46     bool expectClientFail;
     47     const char *hostname;
     48     const char *const *wildcards;
     49 };
     50 
     51 struct QIOChannelTLSHandshakeData {
     52     bool finished;
     53     bool failed;
     54 };
     55 
     56 static void test_tls_handshake_done(QIOTask *task,
     57                                     gpointer opaque)
     58 {
     59     struct QIOChannelTLSHandshakeData *data = opaque;
     60 
     61     data->finished = true;
     62     data->failed = qio_task_propagate_error(task, NULL);
     63 }
     64 
     65 
     66 static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint,
     67                                               const char *certdir)
     68 {
     69     Object *parent = object_get_objects_root();
     70     Object *creds = object_new_with_props(
     71         TYPE_QCRYPTO_TLS_CREDS_X509,
     72         parent,
     73         (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
     74          "testtlscredsserver" : "testtlscredsclient"),
     75         &error_abort,
     76         "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
     77                      "server" : "client"),
     78         "dir", certdir,
     79         "verify-peer", "yes",
     80         "priority", "NORMAL",
     81         /* We skip initial sanity checks here because we
     82          * want to make sure that problems are being
     83          * detected at the TLS session validation stage,
     84          * and the test-crypto-tlscreds test already
     85          * validate the sanity check code.
     86          */
     87         "sanity-check", "no",
     88         NULL
     89         );
     90 
     91     return QCRYPTO_TLS_CREDS(creds);
     92 }
     93 
     94 
     95 /*
     96  * This tests validation checking of peer certificates
     97  *
     98  * This is replicating the checks that are done for an
     99  * active TLS session after handshake completes. To
    100  * simulate that we create our TLS contexts, skipping
    101  * sanity checks. When then get a socketpair, and
    102  * initiate a TLS session across them. Finally do
    103  * do actual cert validation tests
    104  */
    105 static void test_io_channel_tls(const void *opaque)
    106 {
    107     struct QIOChannelTLSTestData *data =
    108         (struct QIOChannelTLSTestData *)opaque;
    109     QCryptoTLSCreds *clientCreds;
    110     QCryptoTLSCreds *serverCreds;
    111     QIOChannelTLS *clientChanTLS;
    112     QIOChannelTLS *serverChanTLS;
    113     QIOChannelSocket *clientChanSock;
    114     QIOChannelSocket *serverChanSock;
    115     QAuthZList *auth;
    116     const char * const *wildcards;
    117     int channel[2];
    118     struct QIOChannelTLSHandshakeData clientHandshake = { false, false };
    119     struct QIOChannelTLSHandshakeData serverHandshake = { false, false };
    120     QIOChannelTest *test;
    121     GMainContext *mainloop;
    122 
    123     /* We'll use this for our fake client-server connection */
    124     g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0);
    125 
    126 #define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/"
    127 #define SERVER_CERT_DIR "tests/test-io-channel-tls-server/"
    128     g_mkdir_with_parents(CLIENT_CERT_DIR, 0700);
    129     g_mkdir_with_parents(SERVER_CERT_DIR, 0700);
    130 
    131     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
    132     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
    133     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
    134 
    135     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
    136     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
    137     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
    138 
    139     g_assert(link(data->servercacrt,
    140                   SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
    141     g_assert(link(data->servercrt,
    142                   SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
    143     g_assert(link(KEYFILE,
    144                   SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
    145 
    146     g_assert(link(data->clientcacrt,
    147                   CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
    148     g_assert(link(data->clientcrt,
    149                   CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
    150     g_assert(link(KEYFILE,
    151                   CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
    152 
    153     clientCreds = test_tls_creds_create(
    154         QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
    155         CLIENT_CERT_DIR);
    156     g_assert(clientCreds != NULL);
    157 
    158     serverCreds = test_tls_creds_create(
    159         QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
    160         SERVER_CERT_DIR);
    161     g_assert(serverCreds != NULL);
    162 
    163     auth = qauthz_list_new("channeltlsacl",
    164                            QAUTHZ_LIST_POLICY_DENY,
    165                            &error_abort);
    166     wildcards = data->wildcards;
    167     while (wildcards && *wildcards) {
    168         qauthz_list_append_rule(auth, *wildcards,
    169                                 QAUTHZ_LIST_POLICY_ALLOW,
    170                                 QAUTHZ_LIST_FORMAT_GLOB,
    171                                 &error_abort);
    172         wildcards++;
    173     }
    174 
    175     clientChanSock = qio_channel_socket_new_fd(
    176         channel[0], &error_abort);
    177     g_assert(clientChanSock != NULL);
    178     serverChanSock = qio_channel_socket_new_fd(
    179         channel[1], &error_abort);
    180     g_assert(serverChanSock != NULL);
    181 
    182     /*
    183      * We have an evil loop to do the handshake in a single
    184      * thread, so we need these non-blocking to avoid deadlock
    185      * of ourselves
    186      */
    187     qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL);
    188     qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL);
    189 
    190     /* Now the real part of the test, setup the sessions */
    191     clientChanTLS = qio_channel_tls_new_client(
    192         QIO_CHANNEL(clientChanSock), clientCreds,
    193         data->hostname, &error_abort);
    194     g_assert(clientChanTLS != NULL);
    195 
    196     serverChanTLS = qio_channel_tls_new_server(
    197         QIO_CHANNEL(serverChanSock), serverCreds,
    198         "channeltlsacl", &error_abort);
    199     g_assert(serverChanTLS != NULL);
    200 
    201     qio_channel_tls_handshake(clientChanTLS,
    202                               test_tls_handshake_done,
    203                               &clientHandshake,
    204                               NULL,
    205                               NULL);
    206     qio_channel_tls_handshake(serverChanTLS,
    207                               test_tls_handshake_done,
    208                               &serverHandshake,
    209                               NULL,
    210                               NULL);
    211 
    212     /*
    213      * Finally we loop around & around doing handshake on each
    214      * session until we get an error, or the handshake completes.
    215      * This relies on the socketpair being nonblocking to avoid
    216      * deadlocking ourselves upon handshake
    217      */
    218     mainloop = g_main_context_default();
    219     do {
    220         g_main_context_iteration(mainloop, TRUE);
    221     } while (!clientHandshake.finished ||
    222              !serverHandshake.finished);
    223 
    224     g_assert(clientHandshake.failed == data->expectClientFail);
    225     g_assert(serverHandshake.failed == data->expectServerFail);
    226 
    227     test = qio_channel_test_new();
    228     qio_channel_test_run_threads(test, false,
    229                                  QIO_CHANNEL(clientChanTLS),
    230                                  QIO_CHANNEL(serverChanTLS));
    231     qio_channel_test_validate(test);
    232 
    233     test = qio_channel_test_new();
    234     qio_channel_test_run_threads(test, true,
    235                                  QIO_CHANNEL(clientChanTLS),
    236                                  QIO_CHANNEL(serverChanTLS));
    237     qio_channel_test_validate(test);
    238 
    239     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
    240     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
    241     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
    242 
    243     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
    244     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
    245     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
    246 
    247     rmdir(CLIENT_CERT_DIR);
    248     rmdir(SERVER_CERT_DIR);
    249 
    250     object_unparent(OBJECT(serverCreds));
    251     object_unparent(OBJECT(clientCreds));
    252 
    253     object_unref(OBJECT(serverChanTLS));
    254     object_unref(OBJECT(clientChanTLS));
    255 
    256     object_unref(OBJECT(serverChanSock));
    257     object_unref(OBJECT(clientChanSock));
    258 
    259     object_unparent(OBJECT(auth));
    260 
    261     close(channel[0]);
    262     close(channel[1]);
    263 }
    264 
    265 
    266 int main(int argc, char **argv)
    267 {
    268     int ret;
    269 
    270     g_assert(qcrypto_init(NULL) == 0);
    271 
    272     module_call_init(MODULE_INIT_QOM);
    273     g_test_init(&argc, &argv, NULL);
    274     g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
    275 
    276     g_mkdir_with_parents(WORKDIR, 0700);
    277 
    278     test_tls_init(KEYFILE);
    279 
    280 # define TEST_CHANNEL(name, caCrt,                                      \
    281                       serverCrt, clientCrt,                             \
    282                       expectServerFail, expectClientFail,               \
    283                       hostname, wildcards)                              \
    284     struct QIOChannelTLSTestData name = {                               \
    285         caCrt, caCrt, serverCrt, clientCrt,                             \
    286         expectServerFail, expectClientFail,                             \
    287         hostname, wildcards                                             \
    288     };                                                                  \
    289     g_test_add_data_func("/qio/channel/tls/" # name,                    \
    290                          &name, test_io_channel_tls);
    291 
    292     /* A perfect CA, perfect client & perfect server */
    293 
    294     /* Basic:CA:critical */
    295     TLS_ROOT_REQ(cacertreq,
    296                  "UK", "qemu CA", NULL, NULL, NULL, NULL,
    297                  true, true, true,
    298                  true, true, GNUTLS_KEY_KEY_CERT_SIGN,
    299                  false, false, NULL, NULL,
    300                  0, 0);
    301     TLS_CERT_REQ(servercertreq, cacertreq,
    302                  "UK", "qemu.org", NULL, NULL, NULL, NULL,
    303                  true, true, false,
    304                  true, true,
    305                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
    306                  true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
    307                  0, 0);
    308     TLS_CERT_REQ(clientcertreq, cacertreq,
    309                  "UK", "qemu", NULL, NULL, NULL, NULL,
    310                  true, true, false,
    311                  true, true,
    312                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
    313                  true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
    314                  0, 0);
    315 
    316     const char *const wildcards[] = {
    317         "C=UK,CN=qemu*",
    318         NULL,
    319     };
    320     TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename,
    321                  clientcertreq.filename, false, false,
    322                  "qemu.org", wildcards);
    323 
    324     ret = g_test_run();
    325 
    326     test_tls_discard_cert(&clientcertreq);
    327     test_tls_discard_cert(&servercertreq);
    328     test_tls_discard_cert(&cacertreq);
    329 
    330     test_tls_cleanup(KEYFILE);
    331     rmdir(WORKDIR);
    332 
    333     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
    334 }