These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / tests / test-crypto-tlssession.c
1 /*
2  * Copyright (C) 2015 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library.  If not, see
16  * <http://www.gnu.org/licenses/>.
17  *
18  * Author: Daniel P. Berrange <berrange@redhat.com>
19  */
20
21 #include "qemu/osdep.h"
22
23 #include "crypto-tls-x509-helpers.h"
24 #include "crypto/tlscredsx509.h"
25 #include "crypto/tlssession.h"
26 #include "qom/object_interfaces.h"
27 #include "qapi/error.h"
28 #include "qemu/sockets.h"
29 #include "qemu/acl.h"
30
31 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
32
33 #define WORKDIR "tests/test-crypto-tlssession-work/"
34 #define KEYFILE WORKDIR "key-ctx.pem"
35
36 struct QCryptoTLSSessionTestData {
37     const char *servercacrt;
38     const char *clientcacrt;
39     const char *servercrt;
40     const char *clientcrt;
41     bool expectServerFail;
42     bool expectClientFail;
43     const char *hostname;
44     const char *const *wildcards;
45 };
46
47
48 static ssize_t testWrite(const char *buf, size_t len, void *opaque)
49 {
50     int *fd = opaque;
51
52     return write(*fd, buf, len);
53 }
54
55 static ssize_t testRead(char *buf, size_t len, void *opaque)
56 {
57     int *fd = opaque;
58
59     return read(*fd, buf, len);
60 }
61
62 static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint,
63                                               const char *certdir,
64                                               Error **errp)
65 {
66     Error *err = NULL;
67     Object *parent = object_get_objects_root();
68     Object *creds = object_new_with_props(
69         TYPE_QCRYPTO_TLS_CREDS_X509,
70         parent,
71         (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
72          "testtlscredsserver" : "testtlscredsclient"),
73         &err,
74         "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
75                      "server" : "client"),
76         "dir", certdir,
77         "verify-peer", "yes",
78         /* We skip initial sanity checks here because we
79          * want to make sure that problems are being
80          * detected at the TLS session validation stage,
81          * and the test-crypto-tlscreds test already
82          * validate the sanity check code.
83          */
84         "sanity-check", "no",
85         NULL
86         );
87
88     if (err) {
89         error_propagate(errp, err);
90         return NULL;
91     }
92     return QCRYPTO_TLS_CREDS(creds);
93 }
94
95
96 /*
97  * This tests validation checking of peer certificates
98  *
99  * This is replicating the checks that are done for an
100  * active TLS session after handshake completes. To
101  * simulate that we create our TLS contexts, skipping
102  * sanity checks. We then get a socketpair, and
103  * initiate a TLS session across them. Finally do
104  * do actual cert validation tests
105  */
106 static void test_crypto_tls_session(const void *opaque)
107 {
108     struct QCryptoTLSSessionTestData *data =
109         (struct QCryptoTLSSessionTestData *)opaque;
110     QCryptoTLSCreds *clientCreds;
111     QCryptoTLSCreds *serverCreds;
112     QCryptoTLSSession *clientSess = NULL;
113     QCryptoTLSSession *serverSess = NULL;
114     qemu_acl *acl;
115     const char * const *wildcards;
116     int channel[2];
117     bool clientShake = false;
118     bool serverShake = false;
119     Error *err = NULL;
120     int ret;
121
122     /* We'll use this for our fake client-server connection */
123     ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel);
124     g_assert(ret == 0);
125
126     /*
127      * We have an evil loop to do the handshake in a single
128      * thread, so we need these non-blocking to avoid deadlock
129      * of ourselves
130      */
131     qemu_set_nonblock(channel[0]);
132     qemu_set_nonblock(channel[1]);
133
134 #define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/"
135 #define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/"
136     mkdir(CLIENT_CERT_DIR, 0700);
137     mkdir(SERVER_CERT_DIR, 0700);
138
139     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
140     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
141     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
142
143     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
144     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
145     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
146
147     g_assert(link(data->servercacrt,
148                   SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
149     g_assert(link(data->servercrt,
150                   SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
151     g_assert(link(KEYFILE,
152                   SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
153
154     g_assert(link(data->clientcacrt,
155                   CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
156     g_assert(link(data->clientcrt,
157                   CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
158     g_assert(link(KEYFILE,
159                   CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
160
161     clientCreds = test_tls_creds_create(
162         QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
163         CLIENT_CERT_DIR,
164         &err);
165     g_assert(clientCreds != NULL);
166
167     serverCreds = test_tls_creds_create(
168         QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
169         SERVER_CERT_DIR,
170         &err);
171     g_assert(serverCreds != NULL);
172
173     acl = qemu_acl_init("tlssessionacl");
174     qemu_acl_reset(acl);
175     wildcards = data->wildcards;
176     while (wildcards && *wildcards) {
177         qemu_acl_append(acl, 0, *wildcards);
178         wildcards++;
179     }
180
181     /* Now the real part of the test, setup the sessions */
182     clientSess = qcrypto_tls_session_new(
183         clientCreds, data->hostname, NULL,
184         QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err);
185     serverSess = qcrypto_tls_session_new(
186         serverCreds, NULL,
187         data->wildcards ? "tlssessionacl" : NULL,
188         QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err);
189
190     g_assert(clientSess != NULL);
191     g_assert(serverSess != NULL);
192
193     /* For handshake to work, we need to set the I/O callbacks
194      * to read/write over the socketpair
195      */
196     qcrypto_tls_session_set_callbacks(serverSess,
197                                       testWrite, testRead,
198                                       &channel[0]);
199     qcrypto_tls_session_set_callbacks(clientSess,
200                                       testWrite, testRead,
201                                       &channel[1]);
202
203     /*
204      * Finally we loop around & around doing handshake on each
205      * session until we get an error, or the handshake completes.
206      * This relies on the socketpair being nonblocking to avoid
207      * deadlocking ourselves upon handshake
208      */
209     do {
210         int rv;
211         if (!serverShake) {
212             rv = qcrypto_tls_session_handshake(serverSess,
213                                                &err);
214             g_assert(rv >= 0);
215             if (qcrypto_tls_session_get_handshake_status(serverSess) ==
216                 QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
217                 serverShake = true;
218             }
219         }
220         if (!clientShake) {
221             rv = qcrypto_tls_session_handshake(clientSess,
222                                                &err);
223             g_assert(rv >= 0);
224             if (qcrypto_tls_session_get_handshake_status(clientSess) ==
225                 QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
226                 clientShake = true;
227             }
228         }
229     } while (!clientShake && !serverShake);
230
231
232     /* Finally make sure the server validation does what
233      * we were expecting
234      */
235     if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) {
236         g_assert(data->expectServerFail);
237         error_free(err);
238         err = NULL;
239     } else {
240         g_assert(!data->expectServerFail);
241     }
242
243     /*
244      * And the same for the client validation check
245      */
246     if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) {
247         g_assert(data->expectClientFail);
248         error_free(err);
249         err = NULL;
250     } else {
251         g_assert(!data->expectClientFail);
252     }
253
254     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
255     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
256     unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
257
258     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
259     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
260     unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
261
262     rmdir(CLIENT_CERT_DIR);
263     rmdir(SERVER_CERT_DIR);
264
265     object_unparent(OBJECT(serverCreds));
266     object_unparent(OBJECT(clientCreds));
267
268     qcrypto_tls_session_free(serverSess);
269     qcrypto_tls_session_free(clientSess);
270
271     close(channel[0]);
272     close(channel[1]);
273 }
274
275
276 int main(int argc, char **argv)
277 {
278     int ret;
279
280     module_call_init(MODULE_INIT_QOM);
281     g_test_init(&argc, &argv, NULL);
282     setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
283
284     mkdir(WORKDIR, 0700);
285
286     test_tls_init(KEYFILE);
287
288 # define TEST_SESS_REG(name, caCrt,                                     \
289                        serverCrt, clientCrt,                            \
290                        expectServerFail, expectClientFail,              \
291                        hostname, wildcards)                             \
292     struct QCryptoTLSSessionTestData name = {                           \
293         caCrt, caCrt, serverCrt, clientCrt,                             \
294         expectServerFail, expectClientFail,                             \
295         hostname, wildcards                                             \
296     };                                                                  \
297     g_test_add_data_func("/qcrypto/tlssession/" # name,                 \
298                          &name, test_crypto_tls_session);               \
299
300
301 # define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt,              \
302                            serverCrt, clientCrt,                        \
303                            expectServerFail, expectClientFail,          \
304                            hostname, wildcards)                         \
305     struct QCryptoTLSSessionTestData name = {                           \
306         serverCaCrt, clientCaCrt, serverCrt, clientCrt,                 \
307         expectServerFail, expectClientFail,                             \
308         hostname, wildcards                                             \
309     };                                                                  \
310     g_test_add_data_func("/qcrypto/tlssession/" # name,                 \
311                          &name, test_crypto_tls_session);               \
312
313     /* A perfect CA, perfect client & perfect server */
314
315     /* Basic:CA:critical */
316     TLS_ROOT_REQ(cacertreq,
317                  "UK", "qemu CA", NULL, NULL, NULL, NULL,
318                  true, true, true,
319                  true, true, GNUTLS_KEY_KEY_CERT_SIGN,
320                  false, false, NULL, NULL,
321                  0, 0);
322
323     TLS_ROOT_REQ(altcacertreq,
324                  "UK", "qemu CA 1", NULL, NULL, NULL, NULL,
325                  true, true, true,
326                  false, false, 0,
327                  false, false, NULL, NULL,
328                  0, 0);
329
330     TLS_CERT_REQ(servercertreq, cacertreq,
331                  "UK", "qemu.org", NULL, NULL, NULL, NULL,
332                  true, true, false,
333                  true, true,
334                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
335                  true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
336                  0, 0);
337     TLS_CERT_REQ(clientcertreq, cacertreq,
338                  "UK", "qemu", NULL, NULL, NULL, NULL,
339                  true, true, false,
340                  true, true,
341                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
342                  true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
343                  0, 0);
344
345     TLS_CERT_REQ(clientcertaltreq, altcacertreq,
346                  "UK", "qemu", NULL, NULL, NULL, NULL,
347                  true, true, false,
348                  true, true,
349                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
350                  true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
351                  0, 0);
352
353     TEST_SESS_REG(basicca, cacertreq.filename,
354                   servercertreq.filename, clientcertreq.filename,
355                   false, false, "qemu.org", NULL);
356     TEST_SESS_REG_EXT(differentca, cacertreq.filename,
357                       altcacertreq.filename, servercertreq.filename,
358                       clientcertaltreq.filename, true, true, "qemu.org", NULL);
359
360
361     /* When an altname is set, the CN is ignored, so it must be duplicated
362      * as an altname for it to match */
363     TLS_CERT_REQ(servercertalt1req, cacertreq,
364                  "UK", "qemu.org", "www.qemu.org", "qemu.org",
365                  "192.168.122.1", "fec0::dead:beaf",
366                  true, true, false,
367                  true, true,
368                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
369                  true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
370                  0, 0);
371     /* This intentionally doesn't replicate */
372     TLS_CERT_REQ(servercertalt2req, cacertreq,
373                  "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org",
374                  "192.168.122.1", "fec0::dead:beaf",
375                  true, true, false,
376                  true, true,
377                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
378                  true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
379                  0, 0);
380
381     TEST_SESS_REG(altname1, cacertreq.filename,
382                   servercertalt1req.filename, clientcertreq.filename,
383                   false, false, "qemu.org", NULL);
384     TEST_SESS_REG(altname2, cacertreq.filename,
385                   servercertalt1req.filename, clientcertreq.filename,
386                   false, false, "www.qemu.org", NULL);
387     TEST_SESS_REG(altname3, cacertreq.filename,
388                   servercertalt1req.filename, clientcertreq.filename,
389                   false, true, "wiki.qemu.org", NULL);
390
391     TEST_SESS_REG(altname4, cacertreq.filename,
392                   servercertalt2req.filename, clientcertreq.filename,
393                   false, true, "qemu.org", NULL);
394     TEST_SESS_REG(altname5, cacertreq.filename,
395                   servercertalt2req.filename, clientcertreq.filename,
396                   false, false, "www.qemu.org", NULL);
397     TEST_SESS_REG(altname6, cacertreq.filename,
398                   servercertalt2req.filename, clientcertreq.filename,
399                   false, false, "wiki.qemu.org", NULL);
400
401     const char *const wildcards1[] = {
402         "C=UK,CN=dogfood",
403         NULL,
404     };
405     const char *const wildcards2[] = {
406         "C=UK,CN=qemu",
407         NULL,
408     };
409     const char *const wildcards3[] = {
410         "C=UK,CN=dogfood",
411         "C=UK,CN=qemu",
412         NULL,
413     };
414     const char *const wildcards4[] = {
415         "C=UK,CN=qemustuff",
416         NULL,
417     };
418     const char *const wildcards5[] = {
419         "C=UK,CN=qemu*",
420         NULL,
421     };
422     const char *const wildcards6[] = {
423         "C=UK,CN=*emu*",
424         NULL,
425     };
426
427     TEST_SESS_REG(wildcard1, cacertreq.filename,
428                   servercertreq.filename, clientcertreq.filename,
429                   true, false, "qemu.org", wildcards1);
430     TEST_SESS_REG(wildcard2, cacertreq.filename,
431                   servercertreq.filename, clientcertreq.filename,
432                   false, false, "qemu.org", wildcards2);
433     TEST_SESS_REG(wildcard3, cacertreq.filename,
434                   servercertreq.filename, clientcertreq.filename,
435                   false, false, "qemu.org", wildcards3);
436     TEST_SESS_REG(wildcard4, cacertreq.filename,
437                   servercertreq.filename, clientcertreq.filename,
438                   true, false, "qemu.org", wildcards4);
439     TEST_SESS_REG(wildcard5, cacertreq.filename,
440                   servercertreq.filename, clientcertreq.filename,
441                   false, false, "qemu.org", wildcards5);
442     TEST_SESS_REG(wildcard6, cacertreq.filename,
443                   servercertreq.filename, clientcertreq.filename,
444                   false, false, "qemu.org", wildcards6);
445
446     TLS_ROOT_REQ(cacertrootreq,
447                  "UK", "qemu root", NULL, NULL, NULL, NULL,
448                  true, true, true,
449                  true, true, GNUTLS_KEY_KEY_CERT_SIGN,
450                  false, false, NULL, NULL,
451                  0, 0);
452     TLS_CERT_REQ(cacertlevel1areq, cacertrootreq,
453                  "UK", "qemu level 1a", NULL, NULL, NULL, NULL,
454                  true, true, true,
455                  true, true, GNUTLS_KEY_KEY_CERT_SIGN,
456                  false, false, NULL, NULL,
457                  0, 0);
458     TLS_CERT_REQ(cacertlevel1breq, cacertrootreq,
459                  "UK", "qemu level 1b", NULL, NULL, NULL, NULL,
460                  true, true, true,
461                  true, true, GNUTLS_KEY_KEY_CERT_SIGN,
462                  false, false, NULL, NULL,
463                  0, 0);
464     TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq,
465                  "UK", "qemu level 2a", NULL, NULL, NULL, NULL,
466                  true, true, true,
467                  true, true, GNUTLS_KEY_KEY_CERT_SIGN,
468                  false, false, NULL, NULL,
469                  0, 0);
470     TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq,
471                  "UK", "qemu.org", NULL, NULL, NULL, NULL,
472                  true, true, false,
473                  true, true,
474                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
475                  true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
476                  0, 0);
477     TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq,
478                  "UK", "qemu client level 2b", NULL, NULL, NULL, NULL,
479                  true, true, false,
480                  true, true,
481                  GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
482                  true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
483                  0, 0);
484
485     gnutls_x509_crt_t certchain[] = {
486         cacertrootreq.crt,
487         cacertlevel1areq.crt,
488         cacertlevel1breq.crt,
489         cacertlevel2areq.crt,
490     };
491
492     test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem",
493                               certchain,
494                               G_N_ELEMENTS(certchain));
495
496     TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem",
497                   servercertlevel3areq.filename, clientcertlevel2breq.filename,
498                   false, false, "qemu.org", NULL);
499
500     ret = g_test_run();
501
502     test_tls_discard_cert(&clientcertreq);
503     test_tls_discard_cert(&clientcertaltreq);
504
505     test_tls_discard_cert(&servercertreq);
506     test_tls_discard_cert(&servercertalt1req);
507     test_tls_discard_cert(&servercertalt2req);
508
509     test_tls_discard_cert(&cacertreq);
510     test_tls_discard_cert(&altcacertreq);
511
512     test_tls_discard_cert(&cacertrootreq);
513     test_tls_discard_cert(&cacertlevel1areq);
514     test_tls_discard_cert(&cacertlevel1breq);
515     test_tls_discard_cert(&cacertlevel2areq);
516     test_tls_discard_cert(&servercertlevel3areq);
517     test_tls_discard_cert(&clientcertlevel2breq);
518     unlink(WORKDIR "cacertchain-sess.pem");
519
520     test_tls_cleanup(KEYFILE);
521     rmdir(WORKDIR);
522
523     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
524 }
525
526 #else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
527
528 int
529 main(void)
530 {
531     return EXIT_SUCCESS;
532 }
533
534 #endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */