mirror of
https://github.com/libvirt/libvirt.git
synced 2026-08-11 05:24:44 -05:00
Fix validation of CA certificate chains
The code added to validate CA certificates did not take into account the possibility that the cacert.pem file can contain multiple (concatenated) cert data blocks. Extend the code for loading CA certs to use the gnutls APIs for loading cert lists. Add test cases to check that multi-level trees of certs will validate correctly. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
@@ -510,6 +510,57 @@ mymain(void)
|
||||
DO_CTX_TEST(true, cacertreq.filename, servercertnew1req.filename, true);
|
||||
DO_CTX_TEST(false, cacertreq.filename, clientcertnew1req.filename, true);
|
||||
|
||||
TLS_ROOT_REQ(cacertrootreq,
|
||||
"UK", "libvirt root", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(cacertlevel1areq, cacertrootreq,
|
||||
"UK", "libvirt level 1a", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(cacertlevel1breq, cacertrootreq,
|
||||
"UK", "libvirt level 1b", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq,
|
||||
"UK", "libvirt level 2a", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq,
|
||||
"UK", "libvirt.org", NULL, NULL, NULL, NULL,
|
||||
true, true, false,
|
||||
true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
|
||||
true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq,
|
||||
"UK", "libvirt client level 2b", NULL, NULL, NULL, NULL,
|
||||
true, true, false,
|
||||
true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
|
||||
true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
|
||||
0, 0);
|
||||
|
||||
gnutls_x509_crt_t certchain[] = {
|
||||
cacertrootreq.crt,
|
||||
cacertlevel1areq.crt,
|
||||
cacertlevel1breq.crt,
|
||||
cacertlevel2areq.crt,
|
||||
};
|
||||
|
||||
testTLSWriteCertChain("cacertchain.pem",
|
||||
certchain,
|
||||
ARRAY_CARDINALITY(certchain));
|
||||
|
||||
DO_CTX_TEST(true, "cacertchain.pem", servercertlevel3areq.filename, false);
|
||||
DO_CTX_TEST(false, "cacertchain.pem", clientcertlevel2breq.filename, false);
|
||||
|
||||
testTLSDiscardCert(&cacertreq);
|
||||
testTLSDiscardCert(&cacert1req);
|
||||
testTLSDiscardCert(&cacert2req);
|
||||
@@ -558,6 +609,14 @@ mymain(void)
|
||||
testTLSDiscardCert(&servercertnew1req);
|
||||
testTLSDiscardCert(&clientcertnew1req);
|
||||
|
||||
testTLSDiscardCert(&cacertrootreq);
|
||||
testTLSDiscardCert(&cacertlevel1areq);
|
||||
testTLSDiscardCert(&cacertlevel1breq);
|
||||
testTLSDiscardCert(&cacertlevel2areq);
|
||||
testTLSDiscardCert(&servercertlevel3areq);
|
||||
testTLSDiscardCert(&clientcertlevel2breq);
|
||||
unlink("cacertchain.pem");
|
||||
|
||||
testTLSCleanup();
|
||||
|
||||
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
|
||||
@@ -406,6 +406,40 @@ testTLSGenerateCert(struct testTLSCertReq *req,
|
||||
}
|
||||
|
||||
|
||||
void testTLSWriteCertChain(const char *filename,
|
||||
gnutls_x509_crt_t *certs,
|
||||
size_t ncerts)
|
||||
{
|
||||
size_t i;
|
||||
int fd;
|
||||
int err;
|
||||
static char buffer[1024*1024];
|
||||
size_t size;
|
||||
|
||||
if ((fd = open(filename, O_WRONLY|O_CREAT, 0600)) < 0) {
|
||||
VIR_WARN("Failed to open %s", filename);
|
||||
abort();
|
||||
}
|
||||
|
||||
for (i = 0; i < ncerts; i++) {
|
||||
size = sizeof(buffer);
|
||||
if ((err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, buffer, &size) < 0)) {
|
||||
VIR_WARN("Failed to export certificate %s", gnutls_strerror(err));
|
||||
unlink(filename);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (safewrite(fd, buffer, size) != size) {
|
||||
VIR_WARN("Failed to write certificate to %s", filename);
|
||||
unlink(filename);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
VIR_FORCE_CLOSE(fd);
|
||||
}
|
||||
|
||||
|
||||
void testTLSDiscardCert(struct testTLSCertReq *req)
|
||||
{
|
||||
if (!req->crt)
|
||||
|
||||
@@ -71,6 +71,9 @@ struct testTLSCertReq {
|
||||
|
||||
void testTLSGenerateCert(struct testTLSCertReq *req,
|
||||
gnutls_x509_crt_t ca);
|
||||
void testTLSWriteCertChain(const char *filename,
|
||||
gnutls_x509_crt_t *certs,
|
||||
size_t ncerts);
|
||||
void testTLSDiscardCert(struct testTLSCertReq *req);
|
||||
|
||||
void testTLSInit(void);
|
||||
|
||||
@@ -193,7 +193,7 @@ static int testTLSSessionInit(const void *opaque)
|
||||
VIR_WARN("Expected server cert check fail");
|
||||
goto cleanup;
|
||||
} else {
|
||||
VIR_DEBUG("Not unexpected server cert fail");
|
||||
VIR_DEBUG("No unexpected server cert fail");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ static int testTLSSessionInit(const void *opaque)
|
||||
VIR_WARN("Expected client cert check fail");
|
||||
goto cleanup;
|
||||
} else {
|
||||
VIR_DEBUG("Not unexpected client cert fail");
|
||||
VIR_DEBUG("No unexpected client cert fail");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +405,57 @@ mymain(void)
|
||||
DO_SESS_TEST(cacertreq.filename, servercertreq.filename, clientcertreq.filename,
|
||||
false, false, "libvirt.org", wildcards6);
|
||||
|
||||
TLS_ROOT_REQ(cacertrootreq,
|
||||
"UK", "libvirt root", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(cacertlevel1areq, cacertrootreq,
|
||||
"UK", "libvirt level 1a", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(cacertlevel1breq, cacertrootreq,
|
||||
"UK", "libvirt level 1b", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq,
|
||||
"UK", "libvirt level 2a", NULL, NULL, NULL, NULL,
|
||||
true, true, true,
|
||||
true, true, GNUTLS_KEY_KEY_CERT_SIGN,
|
||||
false, false, NULL, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq,
|
||||
"UK", "libvirt.org", NULL, NULL, NULL, NULL,
|
||||
true, true, false,
|
||||
true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
|
||||
true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
|
||||
0, 0);
|
||||
TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq,
|
||||
"UK", "libvirt client level 2b", NULL, NULL, NULL, NULL,
|
||||
true, true, false,
|
||||
true, true, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
|
||||
true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
|
||||
0, 0);
|
||||
|
||||
gnutls_x509_crt_t certchain[] = {
|
||||
cacertrootreq.crt,
|
||||
cacertlevel1areq.crt,
|
||||
cacertlevel1breq.crt,
|
||||
cacertlevel2areq.crt,
|
||||
};
|
||||
|
||||
testTLSWriteCertChain("cacertchain.pem",
|
||||
certchain,
|
||||
ARRAY_CARDINALITY(certchain));
|
||||
|
||||
DO_SESS_TEST("cacertchain.pem", servercertlevel3areq.filename, clientcertlevel2breq.filename,
|
||||
false, false, "libvirt.org", NULL);
|
||||
|
||||
testTLSDiscardCert(&clientcertreq);
|
||||
testTLSDiscardCert(&clientcertaltreq);
|
||||
|
||||
@@ -415,6 +466,14 @@ mymain(void)
|
||||
testTLSDiscardCert(&cacertreq);
|
||||
testTLSDiscardCert(&altcacertreq);
|
||||
|
||||
testTLSDiscardCert(&cacertrootreq);
|
||||
testTLSDiscardCert(&cacertlevel1areq);
|
||||
testTLSDiscardCert(&cacertlevel1breq);
|
||||
testTLSDiscardCert(&cacertlevel2areq);
|
||||
testTLSDiscardCert(&servercertlevel3areq);
|
||||
testTLSDiscardCert(&clientcertlevel2breq);
|
||||
unlink("cacertchain.pem");
|
||||
|
||||
testTLSCleanup();
|
||||
|
||||
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
|
||||
Reference in New Issue
Block a user