|
|
1.1 ! root 1: /* ! 2: * QEMU VNC display driver: TLS helpers ! 3: * ! 4: * Copyright (C) 2006 Anthony Liguori <[email protected]> ! 5: * Copyright (C) 2006 Fabrice Bellard ! 6: * Copyright (C) 2009 Red Hat, Inc ! 7: * ! 8: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 9: * of this software and associated documentation files (the "Software"), to deal ! 10: * in the Software without restriction, including without limitation the rights ! 11: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 12: * copies of the Software, and to permit persons to whom the Software is ! 13: * furnished to do so, subject to the following conditions: ! 14: * ! 15: * The above copyright notice and this permission notice shall be included in ! 16: * all copies or substantial portions of the Software. ! 17: * ! 18: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 19: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 20: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 21: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 22: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 23: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 24: * THE SOFTWARE. ! 25: */ ! 26: ! 27: #include "qemu-x509.h" ! 28: #include "vnc.h" ! 29: #include "qemu_socket.h" ! 30: ! 31: #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 ! 32: /* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ ! 33: static void vnc_debug_gnutls_log(int level, const char* str) { ! 34: VNC_DEBUG("%d %s", level, str); ! 35: } ! 36: #endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ ! 37: ! 38: ! 39: #define DH_BITS 1024 ! 40: static gnutls_dh_params_t dh_params; ! 41: ! 42: static int vnc_tls_initialize(void) ! 43: { ! 44: static int tlsinitialized = 0; ! 45: ! 46: if (tlsinitialized) ! 47: return 1; ! 48: ! 49: if (gnutls_global_init () < 0) ! 50: return 0; ! 51: ! 52: /* XXX ought to re-generate diffie-hellmen params periodically */ ! 53: if (gnutls_dh_params_init (&dh_params) < 0) ! 54: return 0; ! 55: if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) ! 56: return 0; ! 57: ! 58: #if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 ! 59: gnutls_global_set_log_level(10); ! 60: gnutls_global_set_log_function(vnc_debug_gnutls_log); ! 61: #endif ! 62: ! 63: tlsinitialized = 1; ! 64: ! 65: return 1; ! 66: } ! 67: ! 68: static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, ! 69: const void *data, ! 70: size_t len) { ! 71: struct VncState *vs = (struct VncState *)transport; ! 72: int ret; ! 73: ! 74: retry: ! 75: ret = send(vs->csock, data, len, 0); ! 76: if (ret < 0) { ! 77: if (errno == EINTR) ! 78: goto retry; ! 79: return -1; ! 80: } ! 81: return ret; ! 82: } ! 83: ! 84: ! 85: static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, ! 86: void *data, ! 87: size_t len) { ! 88: struct VncState *vs = (struct VncState *)transport; ! 89: int ret; ! 90: ! 91: retry: ! 92: ret = recv(vs->csock, data, len, 0); ! 93: if (ret < 0) { ! 94: if (errno == EINTR) ! 95: goto retry; ! 96: return -1; ! 97: } ! 98: return ret; ! 99: } ! 100: ! 101: ! 102: static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void) ! 103: { ! 104: gnutls_anon_server_credentials anon_cred; ! 105: int ret; ! 106: ! 107: if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { ! 108: VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); ! 109: return NULL; ! 110: } ! 111: ! 112: gnutls_anon_set_server_dh_params(anon_cred, dh_params); ! 113: ! 114: return anon_cred; ! 115: } ! 116: ! 117: ! 118: static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) ! 119: { ! 120: gnutls_certificate_credentials_t x509_cred; ! 121: int ret; ! 122: ! 123: if (!vd->tls.x509cacert) { ! 124: VNC_DEBUG("No CA x509 certificate specified\n"); ! 125: return NULL; ! 126: } ! 127: if (!vd->tls.x509cert) { ! 128: VNC_DEBUG("No server x509 certificate specified\n"); ! 129: return NULL; ! 130: } ! 131: if (!vd->tls.x509key) { ! 132: VNC_DEBUG("No server private key specified\n"); ! 133: return NULL; ! 134: } ! 135: ! 136: if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { ! 137: VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); ! 138: return NULL; ! 139: } ! 140: if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, ! 141: vd->tls.x509cacert, ! 142: GNUTLS_X509_FMT_PEM)) < 0) { ! 143: VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); ! 144: gnutls_certificate_free_credentials(x509_cred); ! 145: return NULL; ! 146: } ! 147: ! 148: if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, ! 149: vd->tls.x509cert, ! 150: vd->tls.x509key, ! 151: GNUTLS_X509_FMT_PEM)) < 0) { ! 152: VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); ! 153: gnutls_certificate_free_credentials(x509_cred); ! 154: return NULL; ! 155: } ! 156: ! 157: if (vd->tls.x509cacrl) { ! 158: if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, ! 159: vd->tls.x509cacrl, ! 160: GNUTLS_X509_FMT_PEM)) < 0) { ! 161: VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); ! 162: gnutls_certificate_free_credentials(x509_cred); ! 163: return NULL; ! 164: } ! 165: } ! 166: ! 167: gnutls_certificate_set_dh_params (x509_cred, dh_params); ! 168: ! 169: return x509_cred; ! 170: } ! 171: ! 172: ! 173: int vnc_tls_validate_certificate(struct VncState *vs) ! 174: { ! 175: int ret; ! 176: unsigned int status; ! 177: const gnutls_datum_t *certs; ! 178: unsigned int nCerts, i; ! 179: time_t now; ! 180: ! 181: VNC_DEBUG("Validating client certificate\n"); ! 182: if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { ! 183: VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); ! 184: return -1; ! 185: } ! 186: ! 187: if ((now = time(NULL)) == ((time_t)-1)) { ! 188: return -1; ! 189: } ! 190: ! 191: if (status != 0) { ! 192: if (status & GNUTLS_CERT_INVALID) ! 193: VNC_DEBUG("The certificate is not trusted.\n"); ! 194: ! 195: if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) ! 196: VNC_DEBUG("The certificate hasn't got a known issuer.\n"); ! 197: ! 198: if (status & GNUTLS_CERT_REVOKED) ! 199: VNC_DEBUG("The certificate has been revoked.\n"); ! 200: ! 201: if (status & GNUTLS_CERT_INSECURE_ALGORITHM) ! 202: VNC_DEBUG("The certificate uses an insecure algorithm\n"); ! 203: ! 204: return -1; ! 205: } else { ! 206: VNC_DEBUG("Certificate is valid!\n"); ! 207: } ! 208: ! 209: /* Only support x509 for now */ ! 210: if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) ! 211: return -1; ! 212: ! 213: if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) ! 214: return -1; ! 215: ! 216: for (i = 0 ; i < nCerts ; i++) { ! 217: gnutls_x509_crt_t cert; ! 218: VNC_DEBUG ("Checking certificate chain %d\n", i); ! 219: if (gnutls_x509_crt_init (&cert) < 0) ! 220: return -1; ! 221: ! 222: if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { ! 223: gnutls_x509_crt_deinit (cert); ! 224: return -1; ! 225: } ! 226: ! 227: if (gnutls_x509_crt_get_expiration_time (cert) < now) { ! 228: VNC_DEBUG("The certificate has expired\n"); ! 229: gnutls_x509_crt_deinit (cert); ! 230: return -1; ! 231: } ! 232: ! 233: if (gnutls_x509_crt_get_activation_time (cert) > now) { ! 234: VNC_DEBUG("The certificate is not yet activated\n"); ! 235: gnutls_x509_crt_deinit (cert); ! 236: return -1; ! 237: } ! 238: ! 239: if (gnutls_x509_crt_get_activation_time (cert) > now) { ! 240: VNC_DEBUG("The certificate is not yet activated\n"); ! 241: gnutls_x509_crt_deinit (cert); ! 242: return -1; ! 243: } ! 244: ! 245: if (i == 0) { ! 246: size_t dnameSize = 1024; ! 247: vs->tls.dname = qemu_malloc(dnameSize); ! 248: requery: ! 249: if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) { ! 250: if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { ! 251: vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize); ! 252: goto requery; ! 253: } ! 254: gnutls_x509_crt_deinit (cert); ! 255: VNC_DEBUG("Cannot get client distinguished name: %s", ! 256: gnutls_strerror (ret)); ! 257: return -1; ! 258: } ! 259: ! 260: if (vs->vd->tls.x509verify) { ! 261: int allow; ! 262: if (!vs->vd->tls.acl) { ! 263: VNC_DEBUG("no ACL activated, allowing access"); ! 264: gnutls_x509_crt_deinit (cert); ! 265: continue; ! 266: } ! 267: ! 268: allow = qemu_acl_party_is_allowed(vs->vd->tls.acl, ! 269: vs->tls.dname); ! 270: ! 271: VNC_DEBUG("TLS x509 ACL check for %s is %s\n", ! 272: vs->tls.dname, allow ? "allowed" : "denied"); ! 273: if (!allow) { ! 274: gnutls_x509_crt_deinit (cert); ! 275: return -1; ! 276: } ! 277: } ! 278: } ! 279: ! 280: gnutls_x509_crt_deinit (cert); ! 281: } ! 282: ! 283: return 0; ! 284: } ! 285: ! 286: ! 287: int vnc_tls_client_setup(struct VncState *vs, ! 288: int needX509Creds) { ! 289: static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; ! 290: static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; ! 291: static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0}; ! 292: static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0}; ! 293: ! 294: VNC_DEBUG("Do TLS setup\n"); ! 295: if (vnc_tls_initialize() < 0) { ! 296: VNC_DEBUG("Failed to init TLS\n"); ! 297: vnc_client_error(vs); ! 298: return -1; ! 299: } ! 300: if (vs->tls.session == NULL) { ! 301: if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { ! 302: vnc_client_error(vs); ! 303: return -1; ! 304: } ! 305: ! 306: if (gnutls_set_default_priority(vs->tls.session) < 0) { ! 307: gnutls_deinit(vs->tls.session); ! 308: vs->tls.session = NULL; ! 309: vnc_client_error(vs); ! 310: return -1; ! 311: } ! 312: ! 313: if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) { ! 314: gnutls_deinit(vs->tls.session); ! 315: vs->tls.session = NULL; ! 316: vnc_client_error(vs); ! 317: return -1; ! 318: } ! 319: ! 320: if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) { ! 321: gnutls_deinit(vs->tls.session); ! 322: vs->tls.session = NULL; ! 323: vnc_client_error(vs); ! 324: return -1; ! 325: } ! 326: ! 327: if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) { ! 328: gnutls_deinit(vs->tls.session); ! 329: vs->tls.session = NULL; ! 330: vnc_client_error(vs); ! 331: return -1; ! 332: } ! 333: ! 334: if (needX509Creds) { ! 335: gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); ! 336: if (!x509_cred) { ! 337: gnutls_deinit(vs->tls.session); ! 338: vs->tls.session = NULL; ! 339: vnc_client_error(vs); ! 340: return -1; ! 341: } ! 342: if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { ! 343: gnutls_deinit(vs->tls.session); ! 344: vs->tls.session = NULL; ! 345: gnutls_certificate_free_credentials(x509_cred); ! 346: vnc_client_error(vs); ! 347: return -1; ! 348: } ! 349: if (vs->vd->tls.x509verify) { ! 350: VNC_DEBUG("Requesting a client certificate\n"); ! 351: gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST); ! 352: } ! 353: ! 354: } else { ! 355: gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); ! 356: if (!anon_cred) { ! 357: gnutls_deinit(vs->tls.session); ! 358: vs->tls.session = NULL; ! 359: vnc_client_error(vs); ! 360: return -1; ! 361: } ! 362: if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) { ! 363: gnutls_deinit(vs->tls.session); ! 364: vs->tls.session = NULL; ! 365: gnutls_anon_free_server_credentials(anon_cred); ! 366: vnc_client_error(vs); ! 367: return -1; ! 368: } ! 369: } ! 370: ! 371: gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); ! 372: gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); ! 373: gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); ! 374: } ! 375: return 0; ! 376: } ! 377: ! 378: ! 379: void vnc_tls_client_cleanup(struct VncState *vs) ! 380: { ! 381: if (vs->tls.session) { ! 382: gnutls_deinit(vs->tls.session); ! 383: vs->tls.session = NULL; ! 384: } ! 385: vs->tls.wiremode = VNC_WIREMODE_CLEAR; ! 386: free(vs->tls.dname); ! 387: } ! 388: ! 389: ! 390: ! 391: static int vnc_set_x509_credential(VncDisplay *vd, ! 392: const char *certdir, ! 393: const char *filename, ! 394: char **cred, ! 395: int ignoreMissing) ! 396: { ! 397: struct stat sb; ! 398: ! 399: if (*cred) { ! 400: qemu_free(*cred); ! 401: *cred = NULL; ! 402: } ! 403: ! 404: *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2); ! 405: ! 406: strcpy(*cred, certdir); ! 407: strcat(*cred, "/"); ! 408: strcat(*cred, filename); ! 409: ! 410: VNC_DEBUG("Check %s\n", *cred); ! 411: if (stat(*cred, &sb) < 0) { ! 412: qemu_free(*cred); ! 413: *cred = NULL; ! 414: if (ignoreMissing && errno == ENOENT) ! 415: return 0; ! 416: return -1; ! 417: } ! 418: ! 419: return 0; ! 420: } ! 421: ! 422: ! 423: int vnc_tls_set_x509_creds_dir(VncDisplay *vd, ! 424: const char *certdir) ! 425: { ! 426: if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) ! 427: goto cleanup; ! 428: if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) ! 429: goto cleanup; ! 430: if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) ! 431: goto cleanup; ! 432: if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) ! 433: goto cleanup; ! 434: ! 435: return 0; ! 436: ! 437: cleanup: ! 438: qemu_free(vd->tls.x509cacert); ! 439: qemu_free(vd->tls.x509cacrl); ! 440: qemu_free(vd->tls.x509cert); ! 441: qemu_free(vd->tls.x509key); ! 442: vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; ! 443: return -1; ! 444: } ! 445:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.