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 50 void 51 qcrypto_tls_session_free(QCryptoTLSSession *session) 52 { 53 if (!session) { 54 return; 55 } 56 57 gnutls_deinit(session->handle); 58 g_free(session->hostname); 59 g_free(session->peername); 60 g_free(session->authzid); 61 object_unref(OBJECT(session->creds)); 62 g_free(session); 63 } 64 65 66 static ssize_t 67 qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) 68 { 69 QCryptoTLSSession *session = opaque; 70 71 if (!session->writeFunc) { 72 errno = EIO; 73 return -1; 74 }; 75 76 return session->writeFunc(buf, len, session->opaque); 77 } 78 79 80 static ssize_t 81 qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) 82 { 83 QCryptoTLSSession *session = opaque; 84 85 if (!session->readFunc) { 86 errno = EIO; 87 return -1; 88 }; 89 90 return session->readFunc(buf, len, session->opaque); 91 } 92 93 #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" 94 #define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK" 95 96 QCryptoTLSSession * 97 qcrypto_tls_session_new(QCryptoTLSCreds *creds, 98 const char *hostname, 99 const char *authzid, 100 QCryptoTLSCredsEndpoint endpoint, 101 Error **errp) 102 { 103 QCryptoTLSSession *session; 104 int ret; 105 106 session = g_new0(QCryptoTLSSession, 1); 107 trace_qcrypto_tls_session_new( 108 session, creds, hostname ? hostname : "<none>", 109 authzid ? authzid : "<none>", endpoint); 110 111 if (hostname) { 112 session->hostname = g_strdup(hostname); 113 } 114 if (authzid) { 115 session->authzid = g_strdup(authzid); 116 } 117 session->creds = creds; 118 object_ref(OBJECT(creds)); 119 120 if (creds->endpoint != endpoint) { 121 error_setg(errp, "Credentials endpoint doesn't match session"); 122 goto error; 123 } 124 125 if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 126 ret = gnutls_init(&session->handle, GNUTLS_SERVER); 127 } else { 128 ret = gnutls_init(&session->handle, GNUTLS_CLIENT); 129 } 130 if (ret < 0) { 131 error_setg(errp, "Cannot initialize TLS session: %s", 132 gnutls_strerror(ret)); 133 goto error; 134 } 135 136 if (object_dynamic_cast(OBJECT(creds), 137 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 138 QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); 139 char *prio; 140 141 if (creds->priority != NULL) { 142 prio = g_strdup_printf("%s:%s", 143 creds->priority, 144 TLS_PRIORITY_ADDITIONAL_ANON); 145 } else { 146 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 147 TLS_PRIORITY_ADDITIONAL_ANON); 148 } 149 150 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 151 if (ret < 0) { 152 error_setg(errp, "Unable to set TLS session priority %s: %s", 153 prio, gnutls_strerror(ret)); 154 g_free(prio); 155 goto error; 156 } 157 g_free(prio); 158 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 159 ret = gnutls_credentials_set(session->handle, 160 GNUTLS_CRD_ANON, 161 acreds->data.server); 162 } else { 163 ret = gnutls_credentials_set(session->handle, 164 GNUTLS_CRD_ANON, 165 acreds->data.client); 166 } 167 if (ret < 0) { 168 error_setg(errp, "Cannot set session credentials: %s", 169 gnutls_strerror(ret)); 170 goto error; 171 } 172 } else if (object_dynamic_cast(OBJECT(creds), 173 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 174 QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); 175 char *prio; 176 177 if (creds->priority != NULL) { 178 prio = g_strdup_printf("%s:%s", 179 creds->priority, 180 TLS_PRIORITY_ADDITIONAL_PSK); 181 } else { 182 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 183 TLS_PRIORITY_ADDITIONAL_PSK); 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_PSK, 197 pcreds->data.server); 198 } else { 199 ret = gnutls_credentials_set(session->handle, 200 GNUTLS_CRD_PSK, 201 pcreds->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_X509)) { 210 QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); 211 const char *prio = creds->priority; 212 if (!prio) { 213 prio = CONFIG_TLS_PRIORITY; 214 } 215 216 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 217 if (ret < 0) { 218 error_setg(errp, "Cannot set default TLS session priority %s: %s", 219 prio, gnutls_strerror(ret)); 220 goto error; 221 } 222 ret = gnutls_credentials_set(session->handle, 223 GNUTLS_CRD_CERTIFICATE, 224 tcreds->data); 225 if (ret < 0) { 226 error_setg(errp, "Cannot set session credentials: %s", 227 gnutls_strerror(ret)); 228 goto error; 229 } 230 231 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 232 /* This requests, but does not enforce a client cert. 233 * The cert checking code later does enforcement */ 234 gnutls_certificate_server_set_request(session->handle, 235 GNUTLS_CERT_REQUEST); 236 } 237 } else { 238 error_setg(errp, "Unsupported TLS credentials type %s", 239 object_get_typename(OBJECT(creds))); 240 goto error; 241 } 242 243 gnutls_transport_set_ptr(session->handle, session); 244 gnutls_transport_set_push_function(session->handle, 245 qcrypto_tls_session_push); 246 gnutls_transport_set_pull_function(session->handle, 247 qcrypto_tls_session_pull); 248 249 return session; 250 251 error: 252 qcrypto_tls_session_free(session); 253 return NULL; 254 } 255 256 static int 257 qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, 258 Error **errp) 259 { 260 int ret; 261 unsigned int status; 262 const gnutls_datum_t *certs; 263 unsigned int nCerts, i; 264 time_t now; 265 gnutls_x509_crt_t cert = NULL; 266 Error *err = NULL; 267 268 now = time(NULL); 269 if (now == ((time_t)-1)) { 270 error_setg_errno(errp, errno, "Cannot get current time"); 271 return -1; 272 } 273 274 ret = gnutls_certificate_verify_peers2(session->handle, &status); 275 if (ret < 0) { 276 error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); 277 return -1; 278 } 279 280 if (status != 0) { 281 const char *reason = "Invalid certificate"; 282 283 if (status & GNUTLS_CERT_INVALID) { 284 reason = "The certificate is not trusted"; 285 } 286 287 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 288 reason = "The certificate hasn't got a known issuer"; 289 } 290 291 if (status & GNUTLS_CERT_REVOKED) { 292 reason = "The certificate has been revoked"; 293 } 294 295 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 296 reason = "The certificate uses an insecure algorithm"; 297 } 298 299 error_setg(errp, "%s", reason); 300 return -1; 301 } 302 303 certs = gnutls_certificate_get_peers(session->handle, &nCerts); 304 if (!certs) { 305 error_setg(errp, "No certificate peers"); 306 return -1; 307 } 308 309 for (i = 0; i < nCerts; i++) { 310 ret = gnutls_x509_crt_init(&cert); 311 if (ret < 0) { 312 error_setg(errp, "Cannot initialize certificate: %s", 313 gnutls_strerror(ret)); 314 return -1; 315 } 316 317 ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); 318 if (ret < 0) { 319 error_setg(errp, "Cannot import certificate: %s", 320 gnutls_strerror(ret)); 321 goto error; 322 } 323 324 if (gnutls_x509_crt_get_expiration_time(cert) < now) { 325 error_setg(errp, "The certificate has expired"); 326 goto error; 327 } 328 329 if (gnutls_x509_crt_get_activation_time(cert) > now) { 330 error_setg(errp, "The certificate is not yet activated"); 331 goto error; 332 } 333 334 if (gnutls_x509_crt_get_activation_time(cert) > now) { 335 error_setg(errp, "The certificate is not yet activated"); 336 goto error; 337 } 338 339 if (i == 0) { 340 size_t dnameSize = 1024; 341 session->peername = g_malloc(dnameSize); 342 requery: 343 ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); 344 if (ret < 0) { 345 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { 346 session->peername = g_realloc(session->peername, 347 dnameSize); 348 goto requery; 349 } 350 error_setg(errp, "Cannot get client distinguished name: %s", 351 gnutls_strerror(ret)); 352 goto error; 353 } 354 if (session->authzid) { 355 bool allow; 356 357 allow = qauthz_is_allowed_by_id(session->authzid, 358 session->peername, &err); 359 if (err) { 360 error_propagate(errp, err); 361 goto error; 362 } 363 if (!allow) { 364 error_setg(errp, "TLS x509 authz check for %s is denied", 365 session->peername); 366 goto error; 367 } 368 } 369 if (session->hostname) { 370 if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { 371 error_setg(errp, 372 "Certificate does not match the hostname %s", 373 session->hostname); 374 goto error; 375 } 376 } else { 377 if (session->creds->endpoint == 378 QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { 379 error_setg(errp, "No hostname for certificate validation"); 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 case GNUTLS_E_PREMATURE_TERMINATION: 483 errno = ECONNABORTED; 484 break; 485 default: 486 errno = EIO; 487 break; 488 } 489 ret = -1; 490 } 491 492 return ret; 493 } 494 495 496 int 497 qcrypto_tls_session_handshake(QCryptoTLSSession *session, 498 Error **errp) 499 { 500 int ret = gnutls_handshake(session->handle); 501 if (ret == 0) { 502 session->handshakeComplete = true; 503 } else { 504 if (ret == GNUTLS_E_INTERRUPTED || 505 ret == GNUTLS_E_AGAIN) { 506 ret = 1; 507 } else { 508 error_setg(errp, "TLS handshake failed: %s", 509 gnutls_strerror(ret)); 510 ret = -1; 511 } 512 } 513 514 return ret; 515 } 516 517 518 QCryptoTLSSessionHandshakeStatus 519 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) 520 { 521 if (session->handshakeComplete) { 522 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 523 } else if (gnutls_record_get_direction(session->handle) == 0) { 524 return QCRYPTO_TLS_HANDSHAKE_RECVING; 525 } else { 526 return QCRYPTO_TLS_HANDSHAKE_SENDING; 527 } 528 } 529 530 531 int 532 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 533 Error **errp) 534 { 535 gnutls_cipher_algorithm_t cipher; 536 int ssf; 537 538 cipher = gnutls_cipher_get(session->handle); 539 ssf = gnutls_cipher_get_key_size(cipher); 540 if (!ssf) { 541 error_setg(errp, "Cannot get TLS cipher key size"); 542 return -1; 543 } 544 return ssf; 545 } 546 547 548 char * 549 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 550 { 551 if (session->peername) { 552 return g_strdup(session->peername); 553 } 554 return NULL; 555 } 556 557 558 #else /* ! CONFIG_GNUTLS */ 559 560 561 QCryptoTLSSession * 562 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 563 const char *hostname G_GNUC_UNUSED, 564 const char *authzid G_GNUC_UNUSED, 565 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 566 Error **errp) 567 { 568 error_setg(errp, "TLS requires GNUTLS support"); 569 return NULL; 570 } 571 572 573 void 574 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 575 { 576 } 577 578 579 int 580 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 581 Error **errp) 582 { 583 error_setg(errp, "TLS requires GNUTLS support"); 584 return -1; 585 } 586 587 588 void 589 qcrypto_tls_session_set_callbacks( 590 QCryptoTLSSession *sess G_GNUC_UNUSED, 591 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 592 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 593 void *opaque G_GNUC_UNUSED) 594 { 595 } 596 597 598 ssize_t 599 qcrypto_tls_session_write(QCryptoTLSSession *sess, 600 const char *buf, 601 size_t len) 602 { 603 errno = -EIO; 604 return -1; 605 } 606 607 608 ssize_t 609 qcrypto_tls_session_read(QCryptoTLSSession *sess, 610 char *buf, 611 size_t len) 612 { 613 errno = -EIO; 614 return -1; 615 } 616 617 618 int 619 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 620 Error **errp) 621 { 622 error_setg(errp, "TLS requires GNUTLS support"); 623 return -1; 624 } 625 626 627 QCryptoTLSSessionHandshakeStatus 628 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 629 { 630 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 631 } 632 633 634 int 635 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 636 Error **errp) 637 { 638 error_setg(errp, "TLS requires GNUTLS support"); 639 return -1; 640 } 641 642 643 char * 644 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 645 { 646 return NULL; 647 } 648 649 #endif 650