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 "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 *authzid; 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->authzid); 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 #define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK" 94 95 QCryptoTLSSession * 96 qcrypto_tls_session_new(QCryptoTLSCreds *creds, 97 const char *hostname, 98 const char *authzid, 99 QCryptoTLSCredsEndpoint endpoint, 100 Error **errp) 101 { 102 QCryptoTLSSession *session; 103 int ret; 104 105 session = g_new0(QCryptoTLSSession, 1); 106 trace_qcrypto_tls_session_new( 107 session, creds, hostname ? hostname : "<none>", 108 authzid ? authzid : "<none>", endpoint); 109 110 if (hostname) { 111 session->hostname = g_strdup(hostname); 112 } 113 if (authzid) { 114 session->authzid = g_strdup(authzid); 115 } 116 session->creds = creds; 117 object_ref(OBJECT(creds)); 118 119 if (creds->endpoint != endpoint) { 120 error_setg(errp, "Credentials endpoint doesn't match session"); 121 goto error; 122 } 123 124 if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 125 ret = gnutls_init(&session->handle, GNUTLS_SERVER); 126 } else { 127 ret = gnutls_init(&session->handle, GNUTLS_CLIENT); 128 } 129 if (ret < 0) { 130 error_setg(errp, "Cannot initialize TLS session: %s", 131 gnutls_strerror(ret)); 132 goto error; 133 } 134 135 if (object_dynamic_cast(OBJECT(creds), 136 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 137 QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); 138 char *prio; 139 140 if (creds->priority != NULL) { 141 prio = g_strdup_printf("%s:%s", 142 creds->priority, 143 TLS_PRIORITY_ADDITIONAL_ANON); 144 } else { 145 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 146 TLS_PRIORITY_ADDITIONAL_ANON); 147 } 148 149 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 150 if (ret < 0) { 151 error_setg(errp, "Unable to set TLS session priority %s: %s", 152 prio, gnutls_strerror(ret)); 153 g_free(prio); 154 goto error; 155 } 156 g_free(prio); 157 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 158 ret = gnutls_credentials_set(session->handle, 159 GNUTLS_CRD_ANON, 160 acreds->data.server); 161 } else { 162 ret = gnutls_credentials_set(session->handle, 163 GNUTLS_CRD_ANON, 164 acreds->data.client); 165 } 166 if (ret < 0) { 167 error_setg(errp, "Cannot set session credentials: %s", 168 gnutls_strerror(ret)); 169 goto error; 170 } 171 } else if (object_dynamic_cast(OBJECT(creds), 172 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 173 QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); 174 char *prio; 175 176 if (creds->priority != NULL) { 177 prio = g_strdup_printf("%s:%s", 178 creds->priority, 179 TLS_PRIORITY_ADDITIONAL_PSK); 180 } else { 181 prio = g_strdup(CONFIG_TLS_PRIORITY ":" 182 TLS_PRIORITY_ADDITIONAL_PSK); 183 } 184 185 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 186 if (ret < 0) { 187 error_setg(errp, "Unable to set TLS session priority %s: %s", 188 prio, gnutls_strerror(ret)); 189 g_free(prio); 190 goto error; 191 } 192 g_free(prio); 193 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 194 ret = gnutls_credentials_set(session->handle, 195 GNUTLS_CRD_PSK, 196 pcreds->data.server); 197 } else { 198 ret = gnutls_credentials_set(session->handle, 199 GNUTLS_CRD_PSK, 200 pcreds->data.client); 201 } 202 if (ret < 0) { 203 error_setg(errp, "Cannot set session credentials: %s", 204 gnutls_strerror(ret)); 205 goto error; 206 } 207 } else if (object_dynamic_cast(OBJECT(creds), 208 TYPE_QCRYPTO_TLS_CREDS_X509)) { 209 QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); 210 const char *prio = creds->priority; 211 if (!prio) { 212 prio = CONFIG_TLS_PRIORITY; 213 } 214 215 ret = gnutls_priority_set_direct(session->handle, prio, NULL); 216 if (ret < 0) { 217 error_setg(errp, "Cannot set default TLS session priority %s: %s", 218 prio, gnutls_strerror(ret)); 219 goto error; 220 } 221 ret = gnutls_credentials_set(session->handle, 222 GNUTLS_CRD_CERTIFICATE, 223 tcreds->data); 224 if (ret < 0) { 225 error_setg(errp, "Cannot set session credentials: %s", 226 gnutls_strerror(ret)); 227 goto error; 228 } 229 230 if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 231 /* This requests, but does not enforce a client cert. 232 * The cert checking code later does enforcement */ 233 gnutls_certificate_server_set_request(session->handle, 234 GNUTLS_CERT_REQUEST); 235 } 236 } else { 237 error_setg(errp, "Unsupported TLS credentials type %s", 238 object_get_typename(OBJECT(creds))); 239 goto error; 240 } 241 242 gnutls_transport_set_ptr(session->handle, session); 243 gnutls_transport_set_push_function(session->handle, 244 qcrypto_tls_session_push); 245 gnutls_transport_set_pull_function(session->handle, 246 qcrypto_tls_session_pull); 247 248 return session; 249 250 error: 251 qcrypto_tls_session_free(session); 252 return NULL; 253 } 254 255 static int 256 qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, 257 Error **errp) 258 { 259 int ret; 260 unsigned int status; 261 const gnutls_datum_t *certs; 262 unsigned int nCerts, i; 263 time_t now; 264 gnutls_x509_crt_t cert = NULL; 265 Error *err = NULL; 266 267 now = time(NULL); 268 if (now == ((time_t)-1)) { 269 error_setg_errno(errp, errno, "Cannot get current time"); 270 return -1; 271 } 272 273 ret = gnutls_certificate_verify_peers2(session->handle, &status); 274 if (ret < 0) { 275 error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); 276 return -1; 277 } 278 279 if (status != 0) { 280 const char *reason = "Invalid certificate"; 281 282 if (status & GNUTLS_CERT_INVALID) { 283 reason = "The certificate is not trusted"; 284 } 285 286 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 287 reason = "The certificate hasn't got a known issuer"; 288 } 289 290 if (status & GNUTLS_CERT_REVOKED) { 291 reason = "The certificate has been revoked"; 292 } 293 294 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 295 reason = "The certificate uses an insecure algorithm"; 296 } 297 298 error_setg(errp, "%s", reason); 299 return -1; 300 } 301 302 certs = gnutls_certificate_get_peers(session->handle, &nCerts); 303 if (!certs) { 304 error_setg(errp, "No certificate peers"); 305 return -1; 306 } 307 308 for (i = 0; i < nCerts; i++) { 309 ret = gnutls_x509_crt_init(&cert); 310 if (ret < 0) { 311 error_setg(errp, "Cannot initialize certificate: %s", 312 gnutls_strerror(ret)); 313 return -1; 314 } 315 316 ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); 317 if (ret < 0) { 318 error_setg(errp, "Cannot import certificate: %s", 319 gnutls_strerror(ret)); 320 goto error; 321 } 322 323 if (gnutls_x509_crt_get_expiration_time(cert) < now) { 324 error_setg(errp, "The certificate has expired"); 325 goto error; 326 } 327 328 if (gnutls_x509_crt_get_activation_time(cert) > now) { 329 error_setg(errp, "The certificate is not yet activated"); 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 (i == 0) { 339 size_t dnameSize = 1024; 340 session->peername = g_malloc(dnameSize); 341 requery: 342 ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); 343 if (ret < 0) { 344 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { 345 session->peername = g_realloc(session->peername, 346 dnameSize); 347 goto requery; 348 } 349 error_setg(errp, "Cannot get client distinguished name: %s", 350 gnutls_strerror(ret)); 351 goto error; 352 } 353 if (session->authzid) { 354 bool allow; 355 356 allow = qauthz_is_allowed_by_id(session->authzid, 357 session->peername, &err); 358 if (err) { 359 error_propagate(errp, err); 360 goto error; 361 } 362 if (!allow) { 363 error_setg(errp, "TLS x509 authz check for %s is denied", 364 session->peername); 365 goto error; 366 } 367 } 368 if (session->hostname) { 369 if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { 370 error_setg(errp, 371 "Certificate does not match the hostname %s", 372 session->hostname); 373 goto error; 374 } 375 } 376 } 377 378 gnutls_x509_crt_deinit(cert); 379 } 380 381 return 0; 382 383 error: 384 gnutls_x509_crt_deinit(cert); 385 return -1; 386 } 387 388 389 int 390 qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, 391 Error **errp) 392 { 393 if (object_dynamic_cast(OBJECT(session->creds), 394 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 395 trace_qcrypto_tls_session_check_creds(session, "nop"); 396 return 0; 397 } else if (object_dynamic_cast(OBJECT(session->creds), 398 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 399 trace_qcrypto_tls_session_check_creds(session, "nop"); 400 return 0; 401 } else if (object_dynamic_cast(OBJECT(session->creds), 402 TYPE_QCRYPTO_TLS_CREDS_X509)) { 403 if (session->creds->verifyPeer) { 404 int ret = qcrypto_tls_session_check_certificate(session, 405 errp); 406 trace_qcrypto_tls_session_check_creds(session, 407 ret == 0 ? "pass" : "fail"); 408 return ret; 409 } else { 410 trace_qcrypto_tls_session_check_creds(session, "skip"); 411 return 0; 412 } 413 } else { 414 trace_qcrypto_tls_session_check_creds(session, "error"); 415 error_setg(errp, "Unexpected credential type %s", 416 object_get_typename(OBJECT(session->creds))); 417 return -1; 418 } 419 } 420 421 422 void 423 qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, 424 QCryptoTLSSessionWriteFunc writeFunc, 425 QCryptoTLSSessionReadFunc readFunc, 426 void *opaque) 427 { 428 session->writeFunc = writeFunc; 429 session->readFunc = readFunc; 430 session->opaque = opaque; 431 } 432 433 434 ssize_t 435 qcrypto_tls_session_write(QCryptoTLSSession *session, 436 const char *buf, 437 size_t len) 438 { 439 ssize_t ret = gnutls_record_send(session->handle, buf, len); 440 441 if (ret < 0) { 442 switch (ret) { 443 case GNUTLS_E_AGAIN: 444 errno = EAGAIN; 445 break; 446 case GNUTLS_E_INTERRUPTED: 447 errno = EINTR; 448 break; 449 default: 450 errno = EIO; 451 break; 452 } 453 ret = -1; 454 } 455 456 return ret; 457 } 458 459 460 ssize_t 461 qcrypto_tls_session_read(QCryptoTLSSession *session, 462 char *buf, 463 size_t len) 464 { 465 ssize_t ret = gnutls_record_recv(session->handle, buf, len); 466 467 if (ret < 0) { 468 switch (ret) { 469 case GNUTLS_E_AGAIN: 470 errno = EAGAIN; 471 break; 472 case GNUTLS_E_INTERRUPTED: 473 errno = EINTR; 474 break; 475 case GNUTLS_E_PREMATURE_TERMINATION: 476 errno = ECONNABORTED; 477 break; 478 default: 479 errno = EIO; 480 break; 481 } 482 ret = -1; 483 } 484 485 return ret; 486 } 487 488 489 int 490 qcrypto_tls_session_handshake(QCryptoTLSSession *session, 491 Error **errp) 492 { 493 int ret = gnutls_handshake(session->handle); 494 if (ret == 0) { 495 session->handshakeComplete = true; 496 } else { 497 if (ret == GNUTLS_E_INTERRUPTED || 498 ret == GNUTLS_E_AGAIN) { 499 ret = 1; 500 } else { 501 error_setg(errp, "TLS handshake failed: %s", 502 gnutls_strerror(ret)); 503 ret = -1; 504 } 505 } 506 507 return ret; 508 } 509 510 511 QCryptoTLSSessionHandshakeStatus 512 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) 513 { 514 if (session->handshakeComplete) { 515 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 516 } else if (gnutls_record_get_direction(session->handle) == 0) { 517 return QCRYPTO_TLS_HANDSHAKE_RECVING; 518 } else { 519 return QCRYPTO_TLS_HANDSHAKE_SENDING; 520 } 521 } 522 523 524 int 525 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 526 Error **errp) 527 { 528 gnutls_cipher_algorithm_t cipher; 529 int ssf; 530 531 cipher = gnutls_cipher_get(session->handle); 532 ssf = gnutls_cipher_get_key_size(cipher); 533 if (!ssf) { 534 error_setg(errp, "Cannot get TLS cipher key size"); 535 return -1; 536 } 537 return ssf; 538 } 539 540 541 char * 542 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 543 { 544 if (session->peername) { 545 return g_strdup(session->peername); 546 } 547 return NULL; 548 } 549 550 551 #else /* ! CONFIG_GNUTLS */ 552 553 554 QCryptoTLSSession * 555 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 556 const char *hostname G_GNUC_UNUSED, 557 const char *authzid G_GNUC_UNUSED, 558 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 559 Error **errp) 560 { 561 error_setg(errp, "TLS requires GNUTLS support"); 562 return NULL; 563 } 564 565 566 void 567 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 568 { 569 } 570 571 572 int 573 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 574 Error **errp) 575 { 576 error_setg(errp, "TLS requires GNUTLS support"); 577 return -1; 578 } 579 580 581 void 582 qcrypto_tls_session_set_callbacks( 583 QCryptoTLSSession *sess G_GNUC_UNUSED, 584 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 585 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 586 void *opaque G_GNUC_UNUSED) 587 { 588 } 589 590 591 ssize_t 592 qcrypto_tls_session_write(QCryptoTLSSession *sess, 593 const char *buf, 594 size_t len) 595 { 596 errno = -EIO; 597 return -1; 598 } 599 600 601 ssize_t 602 qcrypto_tls_session_read(QCryptoTLSSession *sess, 603 char *buf, 604 size_t len) 605 { 606 errno = -EIO; 607 return -1; 608 } 609 610 611 int 612 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 613 Error **errp) 614 { 615 error_setg(errp, "TLS requires GNUTLS support"); 616 return -1; 617 } 618 619 620 QCryptoTLSSessionHandshakeStatus 621 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 622 { 623 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 624 } 625 626 627 int 628 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 629 Error **errp) 630 { 631 error_setg(errp, "TLS requires GNUTLS support"); 632 return -1; 633 } 634 635 636 char * 637 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 638 { 639 return NULL; 640 } 641 642 #endif 643