Commit 469b15c68d0b9b00f5d2ab2a16ef5bcf46ca368f

Authored by ths
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
... ... @@ -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) {
... ...