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 == 0) { 550 session->handshakeComplete = true; 551 } else { 552 if (ret == GNUTLS_E_INTERRUPTED || 553 ret == GNUTLS_E_AGAIN) { 554 ret = 1; 555 } else { 556 if (session->rerr || session->werr) { 557 error_setg(errp, "TLS handshake failed: %s: %s", 558 gnutls_strerror(ret), 559 error_get_pretty(session->rerr ? 560 session->rerr : session->werr)); 561 } else { 562 error_setg(errp, "TLS handshake failed: %s", 563 gnutls_strerror(ret)); 564 } 565 ret = -1; 566 } 567 } 568 error_free(session->rerr); 569 error_free(session->werr); 570 session->rerr = session->werr = NULL; 571 572 return ret; 573 } 574 575 576 QCryptoTLSSessionHandshakeStatus 577 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) 578 { 579 if (session->handshakeComplete) { 580 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 581 } else if (gnutls_record_get_direction(session->handle) == 0) { 582 return QCRYPTO_TLS_HANDSHAKE_RECVING; 583 } else { 584 return QCRYPTO_TLS_HANDSHAKE_SENDING; 585 } 586 } 587 588 589 int 590 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 591 Error **errp) 592 { 593 gnutls_cipher_algorithm_t cipher; 594 int ssf; 595 596 cipher = gnutls_cipher_get(session->handle); 597 ssf = gnutls_cipher_get_key_size(cipher); 598 if (!ssf) { 599 error_setg(errp, "Cannot get TLS cipher key size"); 600 return -1; 601 } 602 return ssf; 603 } 604 605 606 char * 607 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 608 { 609 if (session->peername) { 610 return g_strdup(session->peername); 611 } 612 return NULL; 613 } 614 615 616 #else /* ! CONFIG_GNUTLS */ 617 618 619 QCryptoTLSSession * 620 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 621 const char *hostname G_GNUC_UNUSED, 622 const char *authzid G_GNUC_UNUSED, 623 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 624 Error **errp) 625 { 626 error_setg(errp, "TLS requires GNUTLS support"); 627 return NULL; 628 } 629 630 631 void 632 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 633 { 634 } 635 636 637 int 638 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 639 Error **errp) 640 { 641 error_setg(errp, "TLS requires GNUTLS support"); 642 return -1; 643 } 644 645 646 void 647 qcrypto_tls_session_set_callbacks( 648 QCryptoTLSSession *sess G_GNUC_UNUSED, 649 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 650 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 651 void *opaque G_GNUC_UNUSED) 652 { 653 } 654 655 656 ssize_t 657 qcrypto_tls_session_write(QCryptoTLSSession *sess, 658 const char *buf, 659 size_t len, 660 Error **errp) 661 { 662 error_setg(errp, "TLS requires GNUTLS support"); 663 return -1; 664 } 665 666 667 ssize_t 668 qcrypto_tls_session_read(QCryptoTLSSession *sess, 669 char *buf, 670 size_t len, 671 bool gracefulTermination, 672 Error **errp) 673 { 674 error_setg(errp, "TLS requires GNUTLS support"); 675 return -1; 676 } 677 678 679 size_t 680 qcrypto_tls_session_check_pending(QCryptoTLSSession *session) 681 { 682 return 0; 683 } 684 685 686 int 687 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 688 Error **errp) 689 { 690 error_setg(errp, "TLS requires GNUTLS support"); 691 return -1; 692 } 693 694 695 QCryptoTLSSessionHandshakeStatus 696 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 697 { 698 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 699 } 700 701 702 int 703 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 704 Error **errp) 705 { 706 error_setg(errp, "TLS requires GNUTLS support"); 707 return -1; 708 } 709 710 711 char * 712 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 713 { 714 return NULL; 715 } 716 717 #endif 718