2 * Copyright (C) 2011-2012, 2014 Red Hat, Inc.
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.
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.
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/>.
22 #include <sys/socket.h>
24 #include "testutils.h"
25 #include "virnettlshelpers.h"
31 #include "vircommand.h"
32 #include "virsocketaddr.h"
34 #if !defined WIN32 && HAVE_LIBTASN1_H && LIBGNUTLS_VERSION_NUMBER >= 0x020600
36 # define VIR_FROM_THIS VIR_FROM_RPC
38 VIR_LOG_INIT("tests.nettlssessiontest");
40 # define KEYFILE "key-sess.pem"
42 struct testTLSSessionData
{
43 const char *servercacrt
;
44 const char *clientcacrt
;
45 const char *servercrt
;
46 const char *clientcrt
;
47 bool expectServerFail
;
48 bool expectClientFail
;
50 const char *const* wildcards
;
54 static ssize_t
testWrite(const char *buf
, size_t len
, void *opaque
)
58 return write(*fd
, buf
, len
);
61 static ssize_t
testRead(char *buf
, size_t len
, void *opaque
)
65 return read(*fd
, buf
, len
);
69 * This tests validation checking of peer certificates
71 * This is replicating the checks that are done for an
72 * active TLS session after handshake completes. To
73 * simulate that we create our TLS contexts, skipping
74 * sanity checks. We then get a socketpair, and
75 * initiate a TLS session across them. Finally do
76 * actual cert validation tests
78 static int testTLSSessionInit(const void *opaque
)
80 struct testTLSSessionData
*data
= (struct testTLSSessionData
*)opaque
;
81 virNetTLSContextPtr clientCtxt
= NULL
;
82 virNetTLSContextPtr serverCtxt
= NULL
;
83 virNetTLSSessionPtr clientSess
= NULL
;
84 virNetTLSSessionPtr serverSess
= NULL
;
87 bool clientShake
= false;
88 bool serverShake
= false;
91 /* We'll use this for our fake client-server connection */
92 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, channel
) < 0)
96 * We have an evil loop to do the handshake in a single
97 * thread, so we need these non-blocking to avoid deadlock
100 ignore_value(virSetNonBlock(channel
[0]));
101 ignore_value(virSetNonBlock(channel
[1]));
104 /* We skip initial sanity checks here because we
105 * want to make sure that problems are being
106 * detected at the TLS session validation stage
108 serverCtxt
= virNetTLSContextNewServer(data
->servercacrt
,
117 clientCtxt
= virNetTLSContextNewClient(data
->clientcacrt
,
126 VIR_WARN("Unexpected failure loading %s against %s",
127 data
->servercacrt
, data
->servercrt
);
131 VIR_WARN("Unexpected failure loading %s against %s",
132 data
->clientcacrt
, data
->clientcrt
);
137 /* Now the real part of the test, setup the sessions */
138 serverSess
= virNetTLSSessionNew(serverCtxt
, NULL
);
139 clientSess
= virNetTLSSessionNew(clientCtxt
, data
->hostname
);
142 VIR_WARN("Unexpected failure using %s against %s",
143 data
->servercacrt
, data
->servercrt
);
147 VIR_WARN("Unexpected failure using %s against %s",
148 data
->clientcacrt
, data
->clientcrt
);
152 /* For handshake to work, we need to set the I/O callbacks
153 * to read/write over the socketpair
155 virNetTLSSessionSetIOCallbacks(serverSess
, testWrite
, testRead
, &channel
[0]);
156 virNetTLSSessionSetIOCallbacks(clientSess
, testWrite
, testRead
, &channel
[1]);
159 * Finally we loop around & around doing handshake on each
160 * session until we get an error, or the handshake completes.
161 * This relies on the socketpair being nonblocking to avoid
162 * deadlocking ourselves upon handshake
167 rv
= virNetTLSSessionHandshake(serverSess
);
170 if (rv
== VIR_NET_TLS_HANDSHAKE_COMPLETE
)
174 rv
= virNetTLSSessionHandshake(clientSess
);
177 if (rv
== VIR_NET_TLS_HANDSHAKE_COMPLETE
)
180 } while (!clientShake
|| !serverShake
);
183 /* Finally make sure the server validation does what
186 if (virNetTLSContextCheckCertificate(serverCtxt
,
188 if (!data
->expectServerFail
) {
189 VIR_WARN("Unexpected server cert check fail");
192 VIR_DEBUG("Got expected server cert fail");
195 if (data
->expectServerFail
) {
196 VIR_WARN("Expected server cert check fail");
199 VIR_DEBUG("No unexpected server cert fail");
204 * And the same for the client validation check
206 if (virNetTLSContextCheckCertificate(clientCtxt
,
208 if (!data
->expectClientFail
) {
209 VIR_WARN("Unexpected client cert check fail");
212 VIR_DEBUG("Got expected client cert fail");
215 if (data
->expectClientFail
) {
216 VIR_WARN("Expected client cert check fail");
219 VIR_DEBUG("No unexpected client cert fail");
226 virObjectUnref(serverCtxt
);
227 virObjectUnref(clientCtxt
);
228 virObjectUnref(serverSess
);
229 virObjectUnref(clientSess
);
231 VIR_FORCE_CLOSE(channel
[0]);
232 VIR_FORCE_CLOSE(channel
[1]);
242 setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
244 testTLSInit(KEYFILE
);
246 # define DO_SESS_TEST(_caCrt, _serverCrt, _clientCrt, _expectServerFail, \
247 _expectClientFail, _hostname, _wildcards) \
249 static struct testTLSSessionData data; \
250 data.servercacrt = _caCrt; \
251 data.clientcacrt = _caCrt; \
252 data.servercrt = _serverCrt; \
253 data.clientcrt = _clientCrt; \
254 data.expectServerFail = _expectServerFail; \
255 data.expectClientFail = _expectClientFail; \
256 data.hostname = _hostname; \
257 data.wildcards = _wildcards; \
258 if (virTestRun("TLS Session " #_serverCrt " + " #_clientCrt, \
259 testTLSSessionInit, &data) < 0) \
263 # define DO_SESS_TEST_EXT(_serverCaCrt, _clientCaCrt, _serverCrt, _clientCrt, \
264 _expectServerFail, _expectClientFail, \
265 _hostname, _wildcards) \
267 static struct testTLSSessionData data; \
268 data.servercacrt = _serverCaCrt; \
269 data.clientcacrt = _clientCaCrt; \
270 data.servercrt = _serverCrt; \
271 data.clientcrt = _clientCrt; \
272 data.expectServerFail = _expectServerFail; \
273 data.expectClientFail = _expectClientFail; \
274 data.hostname = _hostname; \
275 data.wildcards = _wildcards; \
276 if (virTestRun("TLS Session " #_serverCrt " + " #_clientCrt, \
277 testTLSSessionInit, &data) < 0) \
281 # define TLS_CERT_REQ(varname, cavarname, \
282 co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \
283 kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \
284 static struct testTLSCertReq varname = { \
285 NULL, #varname "-sess.pem", \
286 co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \
287 kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, so \
289 testTLSGenerateCert(&varname, cavarname.crt)
291 # define TLS_ROOT_REQ(varname, \
292 co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \
293 kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, eo) \
294 static struct testTLSCertReq varname = { \
295 NULL, #varname "-sess.pem", \
296 co, cn, an1, an2, ia1, ia2, bce, bcc, bci, \
297 kue, kuc, kuv, kpe, kpc, kpo1, kpo2, so, so \
299 testTLSGenerateCert(&varname, NULL)
301 /* A perfect CA, perfect client & perfect server */
303 /* Basic:CA:critical */
304 TLS_ROOT_REQ(cacertreq
,
305 "UK", "libvirt CA", NULL
, NULL
, NULL
, NULL
,
307 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
308 false, false, NULL
, NULL
,
311 TLS_ROOT_REQ(altcacertreq
,
312 "UK", "libvirt CA 1", NULL
, NULL
, NULL
, NULL
,
315 false, false, NULL
, NULL
,
318 TLS_CERT_REQ(servercertreq
, cacertreq
,
319 "UK", "libvirt.org", NULL
, NULL
, NULL
, NULL
,
321 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
322 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
324 TLS_CERT_REQ(clientcertreq
, cacertreq
,
325 "UK", "libvirt", NULL
, NULL
, NULL
, NULL
,
327 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
328 true, true, GNUTLS_KP_TLS_WWW_CLIENT
, NULL
,
331 TLS_CERT_REQ(clientcertaltreq
, altcacertreq
,
332 "UK", "libvirt", NULL
, NULL
, NULL
, NULL
,
334 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
335 true, true, GNUTLS_KP_TLS_WWW_CLIENT
, NULL
,
338 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
339 false, false, "libvirt.org", NULL
);
340 DO_SESS_TEST_EXT(cacertreq
.filename
, altcacertreq
.filename
, servercertreq
.filename
,
341 clientcertaltreq
.filename
, true, true, "libvirt.org", NULL
);
344 /* When an altname is set, the CN is ignored, so it must be duplicated
345 * as an altname for it to match */
346 TLS_CERT_REQ(servercertalt1req
, cacertreq
,
347 "UK", "libvirt.org", "www.libvirt.org", "libvirt.org", "192.168.122.1", "fec0::dead:beaf",
349 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
350 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
352 /* This intentionally doesn't replicate */
353 TLS_CERT_REQ(servercertalt2req
, cacertreq
,
354 "UK", "libvirt.org", "www.libvirt.org", "wiki.libvirt.org", "192.168.122.1", "fec0::dead:beaf",
356 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
357 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
360 DO_SESS_TEST(cacertreq
.filename
, servercertalt1req
.filename
, clientcertreq
.filename
,
361 false, false, "libvirt.org", NULL
);
362 DO_SESS_TEST(cacertreq
.filename
, servercertalt1req
.filename
, clientcertreq
.filename
,
363 false, false, "www.libvirt.org", NULL
);
364 DO_SESS_TEST(cacertreq
.filename
, servercertalt1req
.filename
, clientcertreq
.filename
,
365 false, true, "wiki.libvirt.org", NULL
);
367 DO_SESS_TEST(cacertreq
.filename
, servercertalt2req
.filename
, clientcertreq
.filename
,
368 false, true, "libvirt.org", NULL
);
369 DO_SESS_TEST(cacertreq
.filename
, servercertalt2req
.filename
, clientcertreq
.filename
,
370 false, false, "www.libvirt.org", NULL
);
371 DO_SESS_TEST(cacertreq
.filename
, servercertalt2req
.filename
, clientcertreq
.filename
,
372 false, false, "wiki.libvirt.org", NULL
);
374 const char *const wildcards1
[] = {
378 const char *const wildcards2
[] = {
382 const char *const wildcards3
[] = {
387 const char *const wildcards4
[] = {
388 "C=UK,CN=libvirtstuff",
391 const char *const wildcards5
[] = {
395 const char *const wildcards6
[] = {
400 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
401 true, false, "libvirt.org", wildcards1
);
402 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
403 false, false, "libvirt.org", wildcards2
);
404 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
405 false, false, "libvirt.org", wildcards3
);
406 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
407 true, false, "libvirt.org", wildcards4
);
408 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
409 false, false, "libvirt.org", wildcards5
);
410 DO_SESS_TEST(cacertreq
.filename
, servercertreq
.filename
, clientcertreq
.filename
,
411 false, false, "libvirt.org", wildcards6
);
413 TLS_ROOT_REQ(cacertrootreq
,
414 "UK", "libvirt root", NULL
, NULL
, NULL
, NULL
,
416 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
417 false, false, NULL
, NULL
,
419 TLS_CERT_REQ(cacertlevel1areq
, cacertrootreq
,
420 "UK", "libvirt level 1a", NULL
, NULL
, NULL
, NULL
,
422 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
423 false, false, NULL
, NULL
,
425 TLS_CERT_REQ(cacertlevel1breq
, cacertrootreq
,
426 "UK", "libvirt level 1b", NULL
, NULL
, NULL
, NULL
,
428 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
429 false, false, NULL
, NULL
,
431 TLS_CERT_REQ(cacertlevel2areq
, cacertlevel1areq
,
432 "UK", "libvirt level 2a", NULL
, NULL
, NULL
, NULL
,
434 true, true, GNUTLS_KEY_KEY_CERT_SIGN
,
435 false, false, NULL
, NULL
,
437 TLS_CERT_REQ(servercertlevel3areq
, cacertlevel2areq
,
438 "UK", "libvirt.org", NULL
, NULL
, NULL
, NULL
,
440 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
441 true, true, GNUTLS_KP_TLS_WWW_SERVER
, NULL
,
443 TLS_CERT_REQ(clientcertlevel2breq
, cacertlevel1breq
,
444 "UK", "libvirt client level 2b", NULL
, NULL
, NULL
, NULL
,
446 true, true, GNUTLS_KEY_DIGITAL_SIGNATURE
| GNUTLS_KEY_KEY_ENCIPHERMENT
,
447 true, true, GNUTLS_KP_TLS_WWW_CLIENT
, NULL
,
450 gnutls_x509_crt_t certchain
[] = {
452 cacertlevel1areq
.crt
,
453 cacertlevel1breq
.crt
,
454 cacertlevel2areq
.crt
,
457 testTLSWriteCertChain("cacertchain-sess.pem",
459 ARRAY_CARDINALITY(certchain
));
461 DO_SESS_TEST("cacertchain-sess.pem", servercertlevel3areq
.filename
, clientcertlevel2breq
.filename
,
462 false, false, "libvirt.org", NULL
);
464 testTLSDiscardCert(&clientcertreq
);
465 testTLSDiscardCert(&clientcertaltreq
);
467 testTLSDiscardCert(&servercertreq
);
468 testTLSDiscardCert(&servercertalt1req
);
469 testTLSDiscardCert(&servercertalt2req
);
471 testTLSDiscardCert(&cacertreq
);
472 testTLSDiscardCert(&altcacertreq
);
474 testTLSDiscardCert(&cacertrootreq
);
475 testTLSDiscardCert(&cacertlevel1areq
);
476 testTLSDiscardCert(&cacertlevel1breq
);
477 testTLSDiscardCert(&cacertlevel2areq
);
478 testTLSDiscardCert(&servercertlevel3areq
);
479 testTLSDiscardCert(&clientcertlevel2breq
);
480 unlink("cacertchain-sess.pem");
482 testTLSCleanup(KEYFILE
);
484 return ret
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
487 VIR_TEST_MAIN_PRELOAD(mymain
, abs_builddir
"/.libs/virrandommock.so")