Commit 469b15c68d0b9b00f5d2ab2a16ef5bcf46ca368f
1 parent
3a702699
x509 client certificate verification, by Daniel P. Berrange.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3138 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
103 additions
and
3 deletions
vnc.c
... | ... | @@ -142,6 +142,7 @@ struct VncState |
142 | 142 | int auth; |
143 | 143 | #if CONFIG_VNC_TLS |
144 | 144 | int subauth; |
145 | + int x509verify; | |
145 | 146 | #endif |
146 | 147 | char challenge[VNC_AUTH_CHALLENGE_SIZE]; |
147 | 148 | |
... | ... | @@ -1427,6 +1428,85 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(void) |
1427 | 1428 | return x509_cred; |
1428 | 1429 | } |
1429 | 1430 | |
1431 | +static int vnc_validate_certificate(struct VncState *vs) | |
1432 | +{ | |
1433 | + int ret; | |
1434 | + unsigned int status; | |
1435 | + const gnutls_datum_t *certs; | |
1436 | + unsigned int nCerts, i; | |
1437 | + time_t now; | |
1438 | + | |
1439 | + VNC_DEBUG("Validating client certificate\n"); | |
1440 | + if ((ret = gnutls_certificate_verify_peers2 (vs->tls_session, &status)) < 0) { | |
1441 | + VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); | |
1442 | + return -1; | |
1443 | + } | |
1444 | + | |
1445 | + if ((now = time(NULL)) == ((time_t)-1)) { | |
1446 | + return -1; | |
1447 | + } | |
1448 | + | |
1449 | + if (status != 0) { | |
1450 | + if (status & GNUTLS_CERT_INVALID) | |
1451 | + VNC_DEBUG("The certificate is not trusted.\n"); | |
1452 | + | |
1453 | + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) | |
1454 | + VNC_DEBUG("The certificate hasn't got a known issuer.\n"); | |
1455 | + | |
1456 | + if (status & GNUTLS_CERT_REVOKED) | |
1457 | + VNC_DEBUG("The certificate has been revoked.\n"); | |
1458 | + | |
1459 | + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) | |
1460 | + VNC_DEBUG("The certificate uses an insecure algorithm\n"); | |
1461 | + | |
1462 | + return -1; | |
1463 | + } else { | |
1464 | + VNC_DEBUG("Certificate is valid!\n"); | |
1465 | + } | |
1466 | + | |
1467 | + /* Only support x509 for now */ | |
1468 | + if (gnutls_certificate_type_get(vs->tls_session) != GNUTLS_CRT_X509) | |
1469 | + return -1; | |
1470 | + | |
1471 | + if (!(certs = gnutls_certificate_get_peers(vs->tls_session, &nCerts))) | |
1472 | + return -1; | |
1473 | + | |
1474 | + for (i = 0 ; i < nCerts ; i++) { | |
1475 | + gnutls_x509_crt_t cert; | |
1476 | + VNC_DEBUG ("Checking certificate chain %d\n", i); | |
1477 | + if (gnutls_x509_crt_init (&cert) < 0) | |
1478 | + return -1; | |
1479 | + | |
1480 | + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { | |
1481 | + gnutls_x509_crt_deinit (cert); | |
1482 | + return -1; | |
1483 | + } | |
1484 | + | |
1485 | + if (gnutls_x509_crt_get_expiration_time (cert) < now) { | |
1486 | + VNC_DEBUG("The certificate has expired\n"); | |
1487 | + gnutls_x509_crt_deinit (cert); | |
1488 | + return -1; | |
1489 | + } | |
1490 | + | |
1491 | + if (gnutls_x509_crt_get_activation_time (cert) > now) { | |
1492 | + VNC_DEBUG("The certificate is not yet activated\n"); | |
1493 | + gnutls_x509_crt_deinit (cert); | |
1494 | + return -1; | |
1495 | + } | |
1496 | + | |
1497 | + if (gnutls_x509_crt_get_activation_time (cert) > now) { | |
1498 | + VNC_DEBUG("The certificate is not yet activated\n"); | |
1499 | + gnutls_x509_crt_deinit (cert); | |
1500 | + return -1; | |
1501 | + } | |
1502 | + | |
1503 | + gnutls_x509_crt_deinit (cert); | |
1504 | + } | |
1505 | + | |
1506 | + return 0; | |
1507 | +} | |
1508 | + | |
1509 | + | |
1430 | 1510 | static int start_auth_vencrypt_subauth(VncState *vs) |
1431 | 1511 | { |
1432 | 1512 | switch (vs->subauth) { |
... | ... | @@ -1475,6 +1555,16 @@ static int vnc_continue_handshake(struct VncState *vs) { |
1475 | 1555 | return -1; |
1476 | 1556 | } |
1477 | 1557 | |
1558 | + if (vs->x509verify) { | |
1559 | + if (vnc_validate_certificate(vs) < 0) { | |
1560 | + VNC_DEBUG("Client verification failed\n"); | |
1561 | + vnc_client_error(vs); | |
1562 | + return -1; | |
1563 | + } else { | |
1564 | + VNC_DEBUG("Client verification passed\n"); | |
1565 | + } | |
1566 | + } | |
1567 | + | |
1478 | 1568 | VNC_DEBUG("Handshake done, switching to TLS data mode\n"); |
1479 | 1569 | vs->wiremode = VNC_WIREMODE_TLS; |
1480 | 1570 | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); |
... | ... | @@ -1556,6 +1646,11 @@ static int vnc_start_tls(struct VncState *vs) { |
1556 | 1646 | vnc_client_error(vs); |
1557 | 1647 | return -1; |
1558 | 1648 | } |
1649 | + if (vs->x509verify) { | |
1650 | + VNC_DEBUG("Requesting a client certificate\n"); | |
1651 | + gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST); | |
1652 | + } | |
1653 | + | |
1559 | 1654 | } else { |
1560 | 1655 | gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); |
1561 | 1656 | if (!anon_cred) { |
... | ... | @@ -1839,6 +1934,7 @@ void vnc_display_close(DisplayState *ds) |
1839 | 1934 | vs->auth = VNC_AUTH_INVALID; |
1840 | 1935 | #if CONFIG_VNC_TLS |
1841 | 1936 | vs->subauth = VNC_AUTH_INVALID; |
1937 | + vs->x509verify = 0; | |
1842 | 1938 | #endif |
1843 | 1939 | } |
1844 | 1940 | |
... | ... | @@ -1885,14 +1981,18 @@ int vnc_display_open(DisplayState *ds, const char *display) |
1885 | 1981 | options = display; |
1886 | 1982 | while ((options = strchr(options, ','))) { |
1887 | 1983 | options++; |
1888 | - if (strncmp(options, "password", 8) == 0) | |
1984 | + if (strncmp(options, "password", 8) == 0) { | |
1889 | 1985 | password = 1; /* Require password auth */ |
1890 | 1986 | #if CONFIG_VNC_TLS |
1891 | - else if (strncmp(options, "tls", 3) == 0) | |
1987 | + } else if (strncmp(options, "tls", 3) == 0) { | |
1892 | 1988 | tls = 1; /* Require TLS */ |
1893 | - else if (strncmp(options, "x509", 4) == 0) | |
1989 | + } else if (strncmp(options, "x509verify", 10) == 0) { | |
1990 | + x509 = 1; /* Require x509 certificates... */ | |
1991 | + vs->x509verify = 1;/* ...and verify client certs */ | |
1992 | + } else if (strncmp(options, "x509", 4) == 0) { | |
1894 | 1993 | x509 = 1; /* Require x509 certificates */ |
1895 | 1994 | #endif |
1995 | + } | |
1896 | 1996 | } |
1897 | 1997 | |
1898 | 1998 | if (password) { | ... | ... |