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.1 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 "authz/base.h" 28 #include "tlscredspriv.h" 29 #include "trace.h" 30 31 #ifdef CONFIG_GNUTLS 32 33 34 #include <gnutls/x509.h> 35 36 37 struct QCryptoTLSSession { 38 QCryptoTLSCreds *creds; 39 gnutls_session_t handle; 40 char *hostname; 41 char *authzid; 42 bool handshakeComplete; 43 QCryptoTLSSessionWriteFunc writeFunc; 44 QCryptoTLSSessionReadFunc readFunc; 45 void *opaque; 46 char *peername; 47 48 /* 49 * Allow concurrent reads and writes, so track 50 * errors separately 51 */ 52 Error *rerr; 53 Error *werr; 54 }; 55 56 57 void 58 qcrypto_tls_session_free(QCryptoTLSSession *session) 59 { 60 if (!session) { 61 return; 62 } 63 64 error_free(session->rerr); 65 error_free(session->werr); 66 67 gnutls_deinit(session->handle); 68 g_free(session->hostname); 69 g_free(session->peername); 70 g_free(session->authzid); 71 object_unref(OBJECT(session->creds)); 72 g_free(session); 73 } 74 75 76 static ssize_t 77 qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) 78 { 79 QCryptoTLSSession *session = opaque; 80 ssize_t ret; 81 82 if (!session->writeFunc) { 83 errno = EIO; 84 return -1; 85 }; 86 87 error_free(session->werr); 88 session->werr = NULL; 89 90 ret = session->writeFunc(buf, len, session->opaque, &session->werr); 91 if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { 92 errno = EAGAIN; 93 return -1; 94 } else if (ret < 0) { 95 errno = EIO; 96 return -1; 97 } else { 98 return ret; 99 } 100 } 101 102 103 static ssize_t 104 qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) 105 { 106 QCryptoTLSSession *session = opaque; 107 ssize_t ret; 108 109 if (!session->readFunc) { 110 errno = EIO; 111 return -1; 112 }; 113 114 error_free(session->rerr); 115 session->rerr = NULL; 116 117 ret = session->readFunc(buf, len, session->opaque, &session->rerr); 118 if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { 119 errno = EAGAIN; 120 return -1; 121 } else if (ret < 0) { 122 errno = EIO; 123 return -1; 124 } else { 125 return ret; 126 } 127 } 128 129 #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" 130 #define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK" 131 132 QCryptoTLSSession * 133 qcrypto_tls_session_new(QCryptoTLSCreds *creds, 134 const char *hostname, 135 const char *authzid, 136 QCryptoTLSCredsEndpoint endpoint, 137 Error **errp) 138 { 139 QCryptoTLSSession *session; 140 int ret; 141 142 session = g_new0(QCryptoTLSSession, 1); 143 trace_qcrypto_tls_session_new( 144 session, creds, hostname ? hostname : "<none>", 145 authzid ? authzid : "<none>", endpoint); 146 147 if (hostname) { 148 session->hostname = g_strdup(hostname); 149 } 150 if (authzid) { 151 session->authzid = g_strdup(authzid); 152 } 153 session->creds = creds; 154 object_ref(OBJECT(creds)); 155 156 if (creds->endpoint != endpoint) { 157 error_setg(errp, "Credentials endpoint doesn't match session"); 158 goto error; 159 } 160 161 if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 162 ret = gnutls_init(&session->handle, GNUTLS_SERVER); 163 } else { 164 ret = gnutls_init(&session->handle, GNUTLS_CLIENT); 165 } 166 if (ret < 0) { 167 error_setg(errp, "Cannot initialize TLS session: %s", 168 gnutls_strerror(ret)); 169 goto error; 170 } 171 172 if (object_dynamic_cast(OBJECT(creds), 173 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 174 QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); 175 char *prio; 176 177 if (creds->priority != NULL) { 178 prio = g_strdup_printf("%s:%s", 179 creds->priority, 180 TLS_PRIORITY_ADDITIONAL_ANON); 181 } else { 182 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 183 TLS_PRIORITY_ADDITIONAL_ANON); 184 } 185 186 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 187 if (ret < 0) { 188 error_setg(errp, "Unable to set TLS session priority %s: %s", 189 prio, gnutls_strerror(ret)); 190 g_free(prio); 191 goto error; 192 } 193 g_free(prio); 194 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 195 ret = gnutls_credentials_set(session->handle, 196 GNUTLS_CRD_ANON, 197 acreds->data.server); 198 } else { 199 ret = gnutls_credentials_set(session->handle, 200 GNUTLS_CRD_ANON, 201 acreds->data.client); 202 } 203 if (ret < 0) { 204 error_setg(errp, "Cannot set session credentials: %s", 205 gnutls_strerror(ret)); 206 goto error; 207 } 208 } else if (object_dynamic_cast(OBJECT(creds), 209 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 210 QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); 211 char *prio; 212 213 if (creds->priority != NULL) { 214 prio = g_strdup_printf("%s:%s", 215 creds->priority, 216 TLS_PRIORITY_ADDITIONAL_PSK); 217 } else { 218 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 219 TLS_PRIORITY_ADDITIONAL_PSK); 220 } 221 222 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 223 if (ret < 0) { 224 error_setg(errp, "Unable to set TLS session priority %s: %s", 225 prio, gnutls_strerror(ret)); 226 g_free(prio); 227 goto error; 228 } 229 g_free(prio); 230 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 231 ret = gnutls_credentials_set(session->handle, 232 GNUTLS_CRD_PSK, 233 pcreds->data.server); 234 } else { 235 ret = gnutls_credentials_set(session->handle, 236 GNUTLS_CRD_PSK, 237 pcreds->data.client); 238 } 239 if (ret < 0) { 240 error_setg(errp, "Cannot set session credentials: %s", 241 gnutls_strerror(ret)); 242 goto error; 243 } 244 } else if (object_dynamic_cast(OBJECT(creds), 245 TYPE_QCRYPTO_TLS_CREDS_X509)) { 246 QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); 247 const char *prio = creds->priority; 248 if (!prio) { 249 prio = CONFIG_TLS_PRIORITY; 250 } 251 252 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 253 if (ret < 0) { 254 error_setg(errp, "Cannot set default TLS session priority %s: %s", 255 prio, gnutls_strerror(ret)); 256 goto error; 257 } 258 ret = gnutls_credentials_set(session->handle, 259 GNUTLS_CRD_CERTIFICATE, 260 tcreds->data); 261 if (ret < 0) { 262 error_setg(errp, "Cannot set session credentials: %s", 263 gnutls_strerror(ret)); 264 goto error; 265 } 266 267 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 268 /* This requests, but does not enforce a client cert. 269 * The cert checking code later does enforcement */ 270 gnutls_certificate_server_set_request(session->handle, 271 GNUTLS_CERT_REQUEST); 272 } 273 } else { 274 error_setg(errp, "Unsupported TLS credentials type %s", 275 object_get_typename(OBJECT(creds))); 276 goto error; 277 } 278 279 gnutls_transport_set_ptr(session->handle, session); 280 gnutls_transport_set_push_function(session->handle, 281 qcrypto_tls_session_push); 282 gnutls_transport_set_pull_function(session->handle, 283 qcrypto_tls_session_pull); 284 285 return session; 286 287 error: 288 qcrypto_tls_session_free(session); 289 return NULL; 290 } 291 292 static int 293 qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, 294 Error **errp) 295 { 296 int ret; 297 unsigned int status; 298 const gnutls_datum_t *certs; 299 unsigned int nCerts, i; 300 time_t now; 301 gnutls_x509_crt_t cert = NULL; 302 Error *err = NULL; 303 304 now = time(NULL); 305 if (now == ((time_t)-1)) { 306 error_setg_errno(errp, errno, "Cannot get current time"); 307 return -1; 308 } 309 310 ret = gnutls_certificate_verify_peers2(session->handle, &status); 311 if (ret < 0) { 312 error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); 313 return -1; 314 } 315 316 if (status != 0) { 317 const char *reason = "Invalid certificate"; 318 319 if (status & GNUTLS_CERT_INVALID) { 320 reason = "The certificate is not trusted"; 321 } 322 323 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 324 reason = "The certificate hasn't got a known issuer"; 325 } 326 327 if (status & GNUTLS_CERT_REVOKED) { 328 reason = "The certificate has been revoked"; 329 } 330 331 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 332 reason = "The certificate uses an insecure algorithm"; 333 } 334 335 error_setg(errp, "%s", reason); 336 return -1; 337 } 338 339 certs = gnutls_certificate_get_peers(session->handle, &nCerts); 340 if (!certs) { 341 error_setg(errp, "No certificate peers"); 342 return -1; 343 } 344 345 for (i = 0; i < nCerts; i++) { 346 ret = gnutls_x509_crt_init(&cert); 347 if (ret < 0) { 348 error_setg(errp, "Cannot initialize certificate: %s", 349 gnutls_strerror(ret)); 350 return -1; 351 } 352 353 ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); 354 if (ret < 0) { 355 error_setg(errp, "Cannot import certificate: %s", 356 gnutls_strerror(ret)); 357 goto error; 358 } 359 360 if (gnutls_x509_crt_get_expiration_time(cert) < now) { 361 error_setg(errp, "The certificate has expired"); 362 goto error; 363 } 364 365 if (gnutls_x509_crt_get_activation_time(cert) > now) { 366 error_setg(errp, "The certificate is not yet activated"); 367 goto error; 368 } 369 370 if (gnutls_x509_crt_get_activation_time(cert) > now) { 371 error_setg(errp, "The certificate is not yet activated"); 372 goto error; 373 } 374 375 if (i == 0) { 376 size_t dnameSize = 1024; 377 session->peername = g_malloc(dnameSize); 378 requery: 379 ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); 380 if (ret < 0) { 381 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { 382 session->peername = g_realloc(session->peername, 383 dnameSize); 384 goto requery; 385 } 386 error_setg(errp, "Cannot get client distinguished name: %s", 387 gnutls_strerror(ret)); 388 goto error; 389 } 390 if (session->authzid) { 391 bool allow; 392 393 allow = qauthz_is_allowed_by_id(session->authzid, 394 session->peername, &err); 395 if (err) { 396 error_propagate(errp, err); 397 goto error; 398 } 399 if (!allow) { 400 error_setg(errp, "TLS x509 authz check for %s is denied", 401 session->peername); 402 goto error; 403 } 404 } 405 if (session->hostname) { 406 if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { 407 error_setg(errp, 408 "Certificate does not match the hostname %s", 409 session->hostname); 410 goto error; 411 } 412 } else { 413 if (session->creds->endpoint == 414 QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { 415 error_setg(errp, "No hostname for certificate validation"); 416 goto error; 417 } 418 } 419 } 420 421 gnutls_x509_crt_deinit(cert); 422 } 423 424 return 0; 425 426 error: 427 gnutls_x509_crt_deinit(cert); 428 return -1; 429 } 430 431 432 int 433 qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, 434 Error **errp) 435 { 436 if (object_dynamic_cast(OBJECT(session->creds), 437 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 438 trace_qcrypto_tls_session_check_creds(session, "nop"); 439 return 0; 440 } else if (object_dynamic_cast(OBJECT(session->creds), 441 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 442 trace_qcrypto_tls_session_check_creds(session, "nop"); 443 return 0; 444 } else if (object_dynamic_cast(OBJECT(session->creds), 445 TYPE_QCRYPTO_TLS_CREDS_X509)) { 446 if (session->creds->verifyPeer) { 447 int ret = qcrypto_tls_session_check_certificate(session, 448 errp); 449 trace_qcrypto_tls_session_check_creds(session, 450 ret == 0 ? "pass" : "fail"); 451 return ret; 452 } else { 453 trace_qcrypto_tls_session_check_creds(session, "skip"); 454 return 0; 455 } 456 } else { 457 trace_qcrypto_tls_session_check_creds(session, "error"); 458 error_setg(errp, "Unexpected credential type %s", 459 object_get_typename(OBJECT(session->creds))); 460 return -1; 461 } 462 } 463 464 465 void 466 qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, 467 QCryptoTLSSessionWriteFunc writeFunc, 468 QCryptoTLSSessionReadFunc readFunc, 469 void *opaque) 470 { 471 session->writeFunc = writeFunc; 472 session->readFunc = readFunc; 473 session->opaque = opaque; 474 } 475 476 477 ssize_t 478 qcrypto_tls_session_write(QCryptoTLSSession *session, 479 const char *buf, 480 size_t len, 481 Error **errp) 482 { 483 ssize_t ret = gnutls_record_send(session->handle, buf, len); 484 485 if (ret < 0) { 486 if (ret == GNUTLS_E_AGAIN) { 487 return QCRYPTO_TLS_SESSION_ERR_BLOCK; 488 } else { 489 if (session->werr) { 490 error_propagate(errp, session->werr); 491 session->werr = NULL; 492 } else { 493 error_setg(errp, 494 "Cannot write to TLS channel: %s", 495 gnutls_strerror(ret)); 496 } 497 return -1; 498 } 499 } 500 501 return ret; 502 } 503 504 505 ssize_t 506 qcrypto_tls_session_read(QCryptoTLSSession *session, 507 char *buf, 508 size_t len, 509 bool gracefulTermination, 510 Error **errp) 511 { 512 ssize_t ret = gnutls_record_recv(session->handle, buf, len); 513 514 if (ret < 0) { 515 if (ret == GNUTLS_E_AGAIN) { 516 return QCRYPTO_TLS_SESSION_ERR_BLOCK; 517 } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) && 518 gracefulTermination){ 519 return 0; 520 } else { 521 if (session->rerr) { 522 error_propagate(errp, session->rerr); 523 session->rerr = NULL; 524 } else { 525 error_setg(errp, 526 "Cannot read from TLS channel: %s", 527 gnutls_strerror(ret)); 528 } 529 return -1; 530 } 531 } 532 533 return ret; 534 } 535 536 537 size_t 538 qcrypto_tls_session_check_pending(QCryptoTLSSession *session) 539 { 540 return gnutls_record_check_pending(session->handle); 541 } 542 543 544 int 545 qcrypto_tls_session_handshake(QCryptoTLSSession *session, 546 Error **errp) 547 { 548 int ret = gnutls_handshake(session->handle); 549 if (!ret) { 550 session->handshakeComplete = true; 551 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 552 } 553 554 if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { 555 int direction = gnutls_record_get_direction(session->handle); 556 return direction ? QCRYPTO_TLS_HANDSHAKE_SENDING : 557 QCRYPTO_TLS_HANDSHAKE_RECVING; 558 } 559 560 if (session->rerr || session->werr) { 561 error_setg(errp, "TLS handshake failed: %s: %s", 562 gnutls_strerror(ret), 563 error_get_pretty(session->rerr ? 564 session->rerr : session->werr)); 565 } else { 566 error_setg(errp, "TLS handshake failed: %s", 567 gnutls_strerror(ret)); 568 } 569 570 error_free(session->rerr); 571 error_free(session->werr); 572 session->rerr = session->werr = NULL; 573 574 return -1; 575 } 576 577 578 int 579 qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) 580 { 581 int ret; 582 583 if (!session->handshakeComplete) { 584 return 0; 585 } 586 587 ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR); 588 589 if (!ret) { 590 return QCRYPTO_TLS_BYE_COMPLETE; 591 } 592 593 if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { 594 int direction = gnutls_record_get_direction(session->handle); 595 return direction ? QCRYPTO_TLS_BYE_SENDING : QCRYPTO_TLS_BYE_RECVING; 596 } 597 598 if (session->rerr || session->werr) { 599 error_setg(errp, "TLS termination failed: %s: %s", gnutls_strerror(ret), 600 error_get_pretty(session->rerr ? 601 session->rerr : session->werr)); 602 } else { 603 error_setg(errp, "TLS termination failed: %s", gnutls_strerror(ret)); 604 } 605 606 error_free(session->rerr); 607 error_free(session->werr); 608 session->rerr = session->werr = NULL; 609 610 return -1; 611 } 612 613 int 614 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 615 Error **errp) 616 { 617 gnutls_cipher_algorithm_t cipher; 618 int ssf; 619 620 cipher = gnutls_cipher_get(session->handle); 621 ssf = gnutls_cipher_get_key_size(cipher); 622 if (!ssf) { 623 error_setg(errp, "Cannot get TLS cipher key size"); 624 return -1; 625 } 626 return ssf; 627 } 628 629 630 char * 631 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 632 { 633 if (session->peername) { 634 return g_strdup(session->peername); 635 } 636 return NULL; 637 } 638 639 640 #else /* ! CONFIG_GNUTLS */ 641 642 643 QCryptoTLSSession * 644 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 645 const char *hostname G_GNUC_UNUSED, 646 const char *authzid G_GNUC_UNUSED, 647 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 648 Error **errp) 649 { 650 error_setg(errp, "TLS requires GNUTLS support"); 651 return NULL; 652 } 653 654 655 void 656 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 657 { 658 } 659 660 661 int 662 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 663 Error **errp) 664 { 665 error_setg(errp, "TLS requires GNUTLS support"); 666 return -1; 667 } 668 669 670 void 671 qcrypto_tls_session_set_callbacks( 672 QCryptoTLSSession *sess G_GNUC_UNUSED, 673 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 674 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 675 void *opaque G_GNUC_UNUSED) 676 { 677 } 678 679 680 ssize_t 681 qcrypto_tls_session_write(QCryptoTLSSession *sess, 682 const char *buf, 683 size_t len, 684 Error **errp) 685 { 686 error_setg(errp, "TLS requires GNUTLS support"); 687 return -1; 688 } 689 690 691 ssize_t 692 qcrypto_tls_session_read(QCryptoTLSSession *sess, 693 char *buf, 694 size_t len, 695 bool gracefulTermination, 696 Error **errp) 697 { 698 error_setg(errp, "TLS requires GNUTLS support"); 699 return -1; 700 } 701 702 703 size_t 704 qcrypto_tls_session_check_pending(QCryptoTLSSession *session) 705 { 706 return 0; 707 } 708 709 710 int 711 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 712 Error **errp) 713 { 714 error_setg(errp, "TLS requires GNUTLS support"); 715 return -1; 716 } 717 718 719 int 720 qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) 721 { 722 return QCRYPTO_TLS_BYE_COMPLETE; 723 } 724 725 726 int 727 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 728 Error **errp) 729 { 730 error_setg(errp, "TLS requires GNUTLS support"); 731 return -1; 732 } 733 734 735 char * 736 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 737 { 738 return NULL; 739 } 740 741 #endif 742