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,6 +142,7 @@ struct VncState | ||
142 | int auth; | 142 | int auth; |
143 | #if CONFIG_VNC_TLS | 143 | #if CONFIG_VNC_TLS |
144 | int subauth; | 144 | int subauth; |
145 | + int x509verify; | ||
145 | #endif | 146 | #endif |
146 | char challenge[VNC_AUTH_CHALLENGE_SIZE]; | 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,6 +1428,85 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(void) | ||
1427 | return x509_cred; | 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 | static int start_auth_vencrypt_subauth(VncState *vs) | 1510 | static int start_auth_vencrypt_subauth(VncState *vs) |
1431 | { | 1511 | { |
1432 | switch (vs->subauth) { | 1512 | switch (vs->subauth) { |
@@ -1475,6 +1555,16 @@ static int vnc_continue_handshake(struct VncState *vs) { | @@ -1475,6 +1555,16 @@ static int vnc_continue_handshake(struct VncState *vs) { | ||
1475 | return -1; | 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 | VNC_DEBUG("Handshake done, switching to TLS data mode\n"); | 1568 | VNC_DEBUG("Handshake done, switching to TLS data mode\n"); |
1479 | vs->wiremode = VNC_WIREMODE_TLS; | 1569 | vs->wiremode = VNC_WIREMODE_TLS; |
1480 | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); | 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,6 +1646,11 @@ static int vnc_start_tls(struct VncState *vs) { | ||
1556 | vnc_client_error(vs); | 1646 | vnc_client_error(vs); |
1557 | return -1; | 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 | } else { | 1654 | } else { |
1560 | gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); | 1655 | gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); |
1561 | if (!anon_cred) { | 1656 | if (!anon_cred) { |
@@ -1839,6 +1934,7 @@ void vnc_display_close(DisplayState *ds) | @@ -1839,6 +1934,7 @@ void vnc_display_close(DisplayState *ds) | ||
1839 | vs->auth = VNC_AUTH_INVALID; | 1934 | vs->auth = VNC_AUTH_INVALID; |
1840 | #if CONFIG_VNC_TLS | 1935 | #if CONFIG_VNC_TLS |
1841 | vs->subauth = VNC_AUTH_INVALID; | 1936 | vs->subauth = VNC_AUTH_INVALID; |
1937 | + vs->x509verify = 0; | ||
1842 | #endif | 1938 | #endif |
1843 | } | 1939 | } |
1844 | 1940 | ||
@@ -1885,14 +1981,18 @@ int vnc_display_open(DisplayState *ds, const char *display) | @@ -1885,14 +1981,18 @@ int vnc_display_open(DisplayState *ds, const char *display) | ||
1885 | options = display; | 1981 | options = display; |
1886 | while ((options = strchr(options, ','))) { | 1982 | while ((options = strchr(options, ','))) { |
1887 | options++; | 1983 | options++; |
1888 | - if (strncmp(options, "password", 8) == 0) | 1984 | + if (strncmp(options, "password", 8) == 0) { |
1889 | password = 1; /* Require password auth */ | 1985 | password = 1; /* Require password auth */ |
1890 | #if CONFIG_VNC_TLS | 1986 | #if CONFIG_VNC_TLS |
1891 | - else if (strncmp(options, "tls", 3) == 0) | 1987 | + } else if (strncmp(options, "tls", 3) == 0) { |
1892 | tls = 1; /* Require TLS */ | 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 | x509 = 1; /* Require x509 certificates */ | 1993 | x509 = 1; /* Require x509 certificates */ |
1895 | #endif | 1994 | #endif |
1995 | + } | ||
1896 | } | 1996 | } |
1897 | 1997 | ||
1898 | if (password) { | 1998 | if (password) { |