1 /* 2 * QEMU crypto TLS session support 3 * 4 * Copyright (c) 2015 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "qemu/osdep.h" 22 #include "crypto/tlssession.h" 23 #include "crypto/tlscredsanon.h" 24 #include "crypto/tlscredspsk.h" 25 #include "crypto/tlscredsx509.h" 26 #include "qapi/error.h" 27 #include "qemu/acl.h" 28 #include "trace.h" 29 30 #ifdef CONFIG_GNUTLS 31 32 33 #include <gnutls/x509.h> 34 35 36 struct QCryptoTLSSession { 37 QCryptoTLSCreds *creds; 38 gnutls_session_t handle; 39 char *hostname; 40 char *aclname; 41 bool handshakeComplete; 42 QCryptoTLSSessionWriteFunc writeFunc; 43 QCryptoTLSSessionReadFunc readFunc; 44 void *opaque; 45 char *peername; 46 }; 47 48 49 void 50 qcrypto_tls_session_free(QCryptoTLSSession *session) 51 { 52 if (!session) { 53 return; 54 } 55 56 gnutls_deinit(session->handle); 57 g_free(session->hostname); 58 g_free(session->peername); 59 g_free(session->aclname); 60 object_unref(OBJECT(session->creds)); 61 g_free(session); 62 } 63 64 65 static ssize_t 66 qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) 67 { 68 QCryptoTLSSession *session = opaque; 69 70 if (!session->writeFunc) { 71 errno = EIO; 72 return -1; 73 }; 74 75 return session->writeFunc(buf, len, session->opaque); 76 } 77 78 79 static ssize_t 80 qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) 81 { 82 QCryptoTLSSession *session = opaque; 83 84 if (!session->readFunc) { 85 errno = EIO; 86 return -1; 87 }; 88 89 return session->readFunc(buf, len, session->opaque); 90 } 91 92 #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" 93 94 #if GNUTLS_VERSION_MAJOR >= 3 95 #define TLS_ECDHE_PSK "+ECDHE-PSK:" 96 #else 97 #define TLS_ECDHE_PSK "" 98 #endif 99 #define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK" 100 101 QCryptoTLSSession * 102 qcrypto_tls_session_new(QCryptoTLSCreds *creds, 103 const char *hostname, 104 const char *aclname, 105 QCryptoTLSCredsEndpoint endpoint, 106 Error **errp) 107 { 108 QCryptoTLSSession *session; 109 int ret; 110 111 session = g_new0(QCryptoTLSSession, 1); 112 trace_qcrypto_tls_session_new( 113 session, creds, hostname ? hostname : "<none>", 114 aclname ? aclname : "<none>", endpoint); 115 116 if (hostname) { 117 session->hostname = g_strdup(hostname); 118 } 119 if (aclname) { 120 session->aclname = g_strdup(aclname); 121 } 122 session->creds = creds; 123 object_ref(OBJECT(creds)); 124 125 if (creds->endpoint != endpoint) { 126 error_setg(errp, "Credentials endpoint doesn't match session"); 127 goto error; 128 } 129 130 if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 131 ret = gnutls_init(&session->handle, GNUTLS_SERVER); 132 } else { 133 ret = gnutls_init(&session->handle, GNUTLS_CLIENT); 134 } 135 if (ret < 0) { 136 error_setg(errp, "Cannot initialize TLS session: %s", 137 gnutls_strerror(ret)); 138 goto error; 139 } 140 141 if (object_dynamic_cast(OBJECT(creds), 142 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 143 QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); 144 char *prio; 145 146 if (creds->priority != NULL) { 147 prio = g_strdup_printf("%s:%s", 148 creds->priority, 149 TLS_PRIORITY_ADDITIONAL_ANON); 150 } else { 151 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 152 TLS_PRIORITY_ADDITIONAL_ANON); 153 } 154 155 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 156 if (ret < 0) { 157 error_setg(errp, "Unable to set TLS session priority %s: %s", 158 prio, gnutls_strerror(ret)); 159 g_free(prio); 160 goto error; 161 } 162 g_free(prio); 163 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 164 ret = gnutls_credentials_set(session->handle, 165 GNUTLS_CRD_ANON, 166 acreds->data.server); 167 } else { 168 ret = gnutls_credentials_set(session->handle, 169 GNUTLS_CRD_ANON, 170 acreds->data.client); 171 } 172 if (ret < 0) { 173 error_setg(errp, "Cannot set session credentials: %s", 174 gnutls_strerror(ret)); 175 goto error; 176 } 177 } else if (object_dynamic_cast(OBJECT(creds), 178 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 179 QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); 180 char *prio; 181 182 if (creds->priority != NULL) { 183 prio = g_strdup_printf("%s:%s", 184 creds->priority, 185 TLS_PRIORITY_ADDITIONAL_PSK); 186 } else { 187 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 188 TLS_PRIORITY_ADDITIONAL_PSK); 189 } 190 191 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 192 if (ret < 0) { 193 error_setg(errp, "Unable to set TLS session priority %s: %s", 194 prio, gnutls_strerror(ret)); 195 g_free(prio); 196 goto error; 197 } 198 g_free(prio); 199 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 200 ret = gnutls_credentials_set(session->handle, 201 GNUTLS_CRD_PSK, 202 pcreds->data.server); 203 } else { 204 ret = gnutls_credentials_set(session->handle, 205 GNUTLS_CRD_PSK, 206 pcreds->data.client); 207 } 208 if (ret < 0) { 209 error_setg(errp, "Cannot set session credentials: %s", 210 gnutls_strerror(ret)); 211 goto error; 212 } 213 } else if (object_dynamic_cast(OBJECT(creds), 214 TYPE_QCRYPTO_TLS_CREDS_X509)) { 215 QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); 216 const char *prio = creds->priority; 217 if (!prio) { 218 prio = CONFIG_TLS_PRIORITY; 219 } 220 221 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 222 if (ret < 0) { 223 error_setg(errp, "Cannot set default TLS session priority %s: %s", 224 prio, gnutls_strerror(ret)); 225 goto error; 226 } 227 ret = gnutls_credentials_set(session->handle, 228 GNUTLS_CRD_CERTIFICATE, 229 tcreds->data); 230 if (ret < 0) { 231 error_setg(errp, "Cannot set session credentials: %s", 232 gnutls_strerror(ret)); 233 goto error; 234 } 235 236 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 237 /* This requests, but does not enforce a client cert. 238 * The cert checking code later does enforcement */ 239 gnutls_certificate_server_set_request(session->handle, 240 GNUTLS_CERT_REQUEST); 241 } 242 } else { 243 error_setg(errp, "Unsupported TLS credentials type %s", 244 object_get_typename(OBJECT(creds))); 245 goto error; 246 } 247 248 gnutls_transport_set_ptr(session->handle, session); 249 gnutls_transport_set_push_function(session->handle, 250 qcrypto_tls_session_push); 251 gnutls_transport_set_pull_function(session->handle, 252 qcrypto_tls_session_pull); 253 254 return session; 255 256 error: 257 qcrypto_tls_session_free(session); 258 return NULL; 259 } 260 261 static int 262 qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, 263 Error **errp) 264 { 265 int ret; 266 unsigned int status; 267 const gnutls_datum_t *certs; 268 unsigned int nCerts, i; 269 time_t now; 270 gnutls_x509_crt_t cert = NULL; 271 272 now = time(NULL); 273 if (now == ((time_t)-1)) { 274 error_setg_errno(errp, errno, "Cannot get current time"); 275 return -1; 276 } 277 278 ret = gnutls_certificate_verify_peers2(session->handle, &status); 279 if (ret < 0) { 280 error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); 281 return -1; 282 } 283 284 if (status != 0) { 285 const char *reason = "Invalid certificate"; 286 287 if (status & GNUTLS_CERT_INVALID) { 288 reason = "The certificate is not trusted"; 289 } 290 291 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 292 reason = "The certificate hasn't got a known issuer"; 293 } 294 295 if (status & GNUTLS_CERT_REVOKED) { 296 reason = "The certificate has been revoked"; 297 } 298 299 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 300 reason = "The certificate uses an insecure algorithm"; 301 } 302 303 error_setg(errp, "%s", reason); 304 return -1; 305 } 306 307 certs = gnutls_certificate_get_peers(session->handle, &nCerts); 308 if (!certs) { 309 error_setg(errp, "No certificate peers"); 310 return -1; 311 } 312 313 for (i = 0; i < nCerts; i++) { 314 ret = gnutls_x509_crt_init(&cert); 315 if (ret < 0) { 316 error_setg(errp, "Cannot initialize certificate: %s", 317 gnutls_strerror(ret)); 318 return -1; 319 } 320 321 ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); 322 if (ret < 0) { 323 error_setg(errp, "Cannot import certificate: %s", 324 gnutls_strerror(ret)); 325 goto error; 326 } 327 328 if (gnutls_x509_crt_get_expiration_time(cert) < now) { 329 error_setg(errp, "The certificate has expired"); 330 goto error; 331 } 332 333 if (gnutls_x509_crt_get_activation_time(cert) > now) { 334 error_setg(errp, "The certificate is not yet activated"); 335 goto error; 336 } 337 338 if (gnutls_x509_crt_get_activation_time(cert) > now) { 339 error_setg(errp, "The certificate is not yet activated"); 340 goto error; 341 } 342 343 if (i == 0) { 344 size_t dnameSize = 1024; 345 session->peername = g_malloc(dnameSize); 346 requery: 347 ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); 348 if (ret < 0) { 349 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { 350 session->peername = g_realloc(session->peername, 351 dnameSize); 352 goto requery; 353 } 354 error_setg(errp, "Cannot get client distinguished name: %s", 355 gnutls_strerror(ret)); 356 goto error; 357 } 358 if (session->aclname) { 359 qemu_acl *acl = qemu_acl_find(session->aclname); 360 int allow; 361 if (!acl) { 362 error_setg(errp, "Cannot find ACL %s", 363 session->aclname); 364 goto error; 365 } 366 367 allow = qemu_acl_party_is_allowed(acl, session->peername); 368 369 if (!allow) { 370 error_setg(errp, "TLS x509 ACL check for %s is denied", 371 session->peername); 372 goto error; 373 } 374 } 375 if (session->hostname) { 376 if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { 377 error_setg(errp, 378 "Certificate does not match the hostname %s", 379 session->hostname); 380 goto error; 381 } 382 } 383 } 384 385 gnutls_x509_crt_deinit(cert); 386 } 387 388 return 0; 389 390 error: 391 gnutls_x509_crt_deinit(cert); 392 return -1; 393 } 394 395 396 int 397 qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, 398 Error **errp) 399 { 400 if (object_dynamic_cast(OBJECT(session->creds), 401 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 402 trace_qcrypto_tls_session_check_creds(session, "nop"); 403 return 0; 404 } else if (object_dynamic_cast(OBJECT(session->creds), 405 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 406 trace_qcrypto_tls_session_check_creds(session, "nop"); 407 return 0; 408 } else if (object_dynamic_cast(OBJECT(session->creds), 409 TYPE_QCRYPTO_TLS_CREDS_X509)) { 410 if (session->creds->verifyPeer) { 411 int ret = qcrypto_tls_session_check_certificate(session, 412 errp); 413 trace_qcrypto_tls_session_check_creds(session, 414 ret == 0 ? "pass" : "fail"); 415 return ret; 416 } else { 417 trace_qcrypto_tls_session_check_creds(session, "skip"); 418 return 0; 419 } 420 } else { 421 trace_qcrypto_tls_session_check_creds(session, "error"); 422 error_setg(errp, "Unexpected credential type %s", 423 object_get_typename(OBJECT(session->creds))); 424 return -1; 425 } 426 } 427 428 429 void 430 qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, 431 QCryptoTLSSessionWriteFunc writeFunc, 432 QCryptoTLSSessionReadFunc readFunc, 433 void *opaque) 434 { 435 session->writeFunc = writeFunc; 436 session->readFunc = readFunc; 437 session->opaque = opaque; 438 } 439 440 441 ssize_t 442 qcrypto_tls_session_write(QCryptoTLSSession *session, 443 const char *buf, 444 size_t len) 445 { 446 ssize_t ret = gnutls_record_send(session->handle, buf, len); 447 448 if (ret < 0) { 449 switch (ret) { 450 case GNUTLS_E_AGAIN: 451 errno = EAGAIN; 452 break; 453 case GNUTLS_E_INTERRUPTED: 454 errno = EINTR; 455 break; 456 default: 457 errno = EIO; 458 break; 459 } 460 ret = -1; 461 } 462 463 return ret; 464 } 465 466 467 ssize_t 468 qcrypto_tls_session_read(QCryptoTLSSession *session, 469 char *buf, 470 size_t len) 471 { 472 ssize_t ret = gnutls_record_recv(session->handle, buf, len); 473 474 if (ret < 0) { 475 switch (ret) { 476 case GNUTLS_E_AGAIN: 477 errno = EAGAIN; 478 break; 479 case GNUTLS_E_INTERRUPTED: 480 errno = EINTR; 481 break; 482 default: 483 errno = EIO; 484 break; 485 } 486 ret = -1; 487 } 488 489 return ret; 490 } 491 492 493 int 494 qcrypto_tls_session_handshake(QCryptoTLSSession *session, 495 Error **errp) 496 { 497 int ret = gnutls_handshake(session->handle); 498 if (ret == 0) { 499 session->handshakeComplete = true; 500 } else { 501 if (ret == GNUTLS_E_INTERRUPTED || 502 ret == GNUTLS_E_AGAIN) { 503 ret = 1; 504 } else { 505 error_setg(errp, "TLS handshake failed: %s", 506 gnutls_strerror(ret)); 507 ret = -1; 508 } 509 } 510 511 return ret; 512 } 513 514 515 QCryptoTLSSessionHandshakeStatus 516 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) 517 { 518 if (session->handshakeComplete) { 519 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 520 } else if (gnutls_record_get_direction(session->handle) == 0) { 521 return QCRYPTO_TLS_HANDSHAKE_RECVING; 522 } else { 523 return QCRYPTO_TLS_HANDSHAKE_SENDING; 524 } 525 } 526 527 528 int 529 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 530 Error **errp) 531 { 532 gnutls_cipher_algorithm_t cipher; 533 int ssf; 534 535 cipher = gnutls_cipher_get(session->handle); 536 ssf = gnutls_cipher_get_key_size(cipher); 537 if (!ssf) { 538 error_setg(errp, "Cannot get TLS cipher key size"); 539 return -1; 540 } 541 return ssf; 542 } 543 544 545 char * 546 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 547 { 548 if (session->peername) { 549 return g_strdup(session->peername); 550 } 551 return NULL; 552 } 553 554 555 #else /* ! CONFIG_GNUTLS */ 556 557 558 QCryptoTLSSession * 559 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 560 const char *hostname G_GNUC_UNUSED, 561 const char *aclname G_GNUC_UNUSED, 562 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 563 Error **errp) 564 { 565 error_setg(errp, "TLS requires GNUTLS support"); 566 return NULL; 567 } 568 569 570 void 571 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 572 { 573 } 574 575 576 int 577 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 578 Error **errp) 579 { 580 error_setg(errp, "TLS requires GNUTLS support"); 581 return -1; 582 } 583 584 585 void 586 qcrypto_tls_session_set_callbacks( 587 QCryptoTLSSession *sess G_GNUC_UNUSED, 588 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 589 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 590 void *opaque G_GNUC_UNUSED) 591 { 592 } 593 594 595 ssize_t 596 qcrypto_tls_session_write(QCryptoTLSSession *sess, 597 const char *buf, 598 size_t len) 599 { 600 errno = -EIO; 601 return -1; 602 } 603 604 605 ssize_t 606 qcrypto_tls_session_read(QCryptoTLSSession *sess, 607 char *buf, 608 size_t len) 609 { 610 errno = -EIO; 611 return -1; 612 } 613 614 615 int 616 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 617 Error **errp) 618 { 619 error_setg(errp, "TLS requires GNUTLS support"); 620 return -1; 621 } 622 623 624 QCryptoTLSSessionHandshakeStatus 625 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 626 { 627 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 628 } 629 630 631 int 632 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 633 Error **errp) 634 { 635 error_setg(errp, "TLS requires GNUTLS support"); 636 return -1; 637 } 638 639 640 char * 641 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 642 { 643 return NULL; 644 } 645 646 #endif 647