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 #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 *aclname, 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 aclname ? aclname : "<none>", endpoint); 109 110 if (hostname) { 111 session->hostname = g_strdup(hostname); 112 } 113 if (aclname) { 114 session->aclname = g_strdup(aclname); 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 266 now = time(NULL); 267 if (now == ((time_t)-1)) { 268 error_setg_errno(errp, errno, "Cannot get current time"); 269 return -1; 270 } 271 272 ret = gnutls_certificate_verify_peers2(session->handle, &status); 273 if (ret < 0) { 274 error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); 275 return -1; 276 } 277 278 if (status != 0) { 279 const char *reason = "Invalid certificate"; 280 281 if (status & GNUTLS_CERT_INVALID) { 282 reason = "The certificate is not trusted"; 283 } 284 285 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 286 reason = "The certificate hasn't got a known issuer"; 287 } 288 289 if (status & GNUTLS_CERT_REVOKED) { 290 reason = "The certificate has been revoked"; 291 } 292 293 if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { 294 reason = "The certificate uses an insecure algorithm"; 295 } 296 297 error_setg(errp, "%s", reason); 298 return -1; 299 } 300 301 certs = gnutls_certificate_get_peers(session->handle, &nCerts); 302 if (!certs) { 303 error_setg(errp, "No certificate peers"); 304 return -1; 305 } 306 307 for (i = 0; i < nCerts; i++) { 308 ret = gnutls_x509_crt_init(&cert); 309 if (ret < 0) { 310 error_setg(errp, "Cannot initialize certificate: %s", 311 gnutls_strerror(ret)); 312 return -1; 313 } 314 315 ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); 316 if (ret < 0) { 317 error_setg(errp, "Cannot import certificate: %s", 318 gnutls_strerror(ret)); 319 goto error; 320 } 321 322 if (gnutls_x509_crt_get_expiration_time(cert) < now) { 323 error_setg(errp, "The certificate has expired"); 324 goto error; 325 } 326 327 if (gnutls_x509_crt_get_activation_time(cert) > now) { 328 error_setg(errp, "The certificate is not yet activated"); 329 goto error; 330 } 331 332 if (gnutls_x509_crt_get_activation_time(cert) > now) { 333 error_setg(errp, "The certificate is not yet activated"); 334 goto error; 335 } 336 337 if (i == 0) { 338 size_t dnameSize = 1024; 339 session->peername = g_malloc(dnameSize); 340 requery: 341 ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); 342 if (ret < 0) { 343 if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { 344 session->peername = g_realloc(session->peername, 345 dnameSize); 346 goto requery; 347 } 348 error_setg(errp, "Cannot get client distinguished name: %s", 349 gnutls_strerror(ret)); 350 goto error; 351 } 352 if (session->aclname) { 353 qemu_acl *acl = qemu_acl_find(session->aclname); 354 int allow; 355 if (!acl) { 356 error_setg(errp, "Cannot find ACL %s", 357 session->aclname); 358 goto error; 359 } 360 361 allow = qemu_acl_party_is_allowed(acl, session->peername); 362 363 if (!allow) { 364 error_setg(errp, "TLS x509 ACL 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 } 377 } 378 379 gnutls_x509_crt_deinit(cert); 380 } 381 382 return 0; 383 384 error: 385 gnutls_x509_crt_deinit(cert); 386 return -1; 387 } 388 389 390 int 391 qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, 392 Error **errp) 393 { 394 if (object_dynamic_cast(OBJECT(session->creds), 395 TYPE_QCRYPTO_TLS_CREDS_ANON)) { 396 trace_qcrypto_tls_session_check_creds(session, "nop"); 397 return 0; 398 } else if (object_dynamic_cast(OBJECT(session->creds), 399 TYPE_QCRYPTO_TLS_CREDS_PSK)) { 400 trace_qcrypto_tls_session_check_creds(session, "nop"); 401 return 0; 402 } else if (object_dynamic_cast(OBJECT(session->creds), 403 TYPE_QCRYPTO_TLS_CREDS_X509)) { 404 if (session->creds->verifyPeer) { 405 int ret = qcrypto_tls_session_check_certificate(session, 406 errp); 407 trace_qcrypto_tls_session_check_creds(session, 408 ret == 0 ? "pass" : "fail"); 409 return ret; 410 } else { 411 trace_qcrypto_tls_session_check_creds(session, "skip"); 412 return 0; 413 } 414 } else { 415 trace_qcrypto_tls_session_check_creds(session, "error"); 416 error_setg(errp, "Unexpected credential type %s", 417 object_get_typename(OBJECT(session->creds))); 418 return -1; 419 } 420 } 421 422 423 void 424 qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, 425 QCryptoTLSSessionWriteFunc writeFunc, 426 QCryptoTLSSessionReadFunc readFunc, 427 void *opaque) 428 { 429 session->writeFunc = writeFunc; 430 session->readFunc = readFunc; 431 session->opaque = opaque; 432 } 433 434 435 ssize_t 436 qcrypto_tls_session_write(QCryptoTLSSession *session, 437 const char *buf, 438 size_t len) 439 { 440 ssize_t ret = gnutls_record_send(session->handle, buf, len); 441 442 if (ret < 0) { 443 switch (ret) { 444 case GNUTLS_E_AGAIN: 445 errno = EAGAIN; 446 break; 447 case GNUTLS_E_INTERRUPTED: 448 errno = EINTR; 449 break; 450 default: 451 errno = EIO; 452 break; 453 } 454 ret = -1; 455 } 456 457 return ret; 458 } 459 460 461 ssize_t 462 qcrypto_tls_session_read(QCryptoTLSSession *session, 463 char *buf, 464 size_t len) 465 { 466 ssize_t ret = gnutls_record_recv(session->handle, buf, len); 467 468 if (ret < 0) { 469 switch (ret) { 470 case GNUTLS_E_AGAIN: 471 errno = EAGAIN; 472 break; 473 case GNUTLS_E_INTERRUPTED: 474 errno = EINTR; 475 break; 476 default: 477 errno = EIO; 478 break; 479 } 480 ret = -1; 481 } 482 483 return ret; 484 } 485 486 487 int 488 qcrypto_tls_session_handshake(QCryptoTLSSession *session, 489 Error **errp) 490 { 491 int ret = gnutls_handshake(session->handle); 492 if (ret == 0) { 493 session->handshakeComplete = true; 494 } else { 495 if (ret == GNUTLS_E_INTERRUPTED || 496 ret == GNUTLS_E_AGAIN) { 497 ret = 1; 498 } else { 499 error_setg(errp, "TLS handshake failed: %s", 500 gnutls_strerror(ret)); 501 ret = -1; 502 } 503 } 504 505 return ret; 506 } 507 508 509 QCryptoTLSSessionHandshakeStatus 510 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) 511 { 512 if (session->handshakeComplete) { 513 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 514 } else if (gnutls_record_get_direction(session->handle) == 0) { 515 return QCRYPTO_TLS_HANDSHAKE_RECVING; 516 } else { 517 return QCRYPTO_TLS_HANDSHAKE_SENDING; 518 } 519 } 520 521 522 int 523 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 524 Error **errp) 525 { 526 gnutls_cipher_algorithm_t cipher; 527 int ssf; 528 529 cipher = gnutls_cipher_get(session->handle); 530 ssf = gnutls_cipher_get_key_size(cipher); 531 if (!ssf) { 532 error_setg(errp, "Cannot get TLS cipher key size"); 533 return -1; 534 } 535 return ssf; 536 } 537 538 539 char * 540 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 541 { 542 if (session->peername) { 543 return g_strdup(session->peername); 544 } 545 return NULL; 546 } 547 548 549 #else /* ! CONFIG_GNUTLS */ 550 551 552 QCryptoTLSSession * 553 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 554 const char *hostname G_GNUC_UNUSED, 555 const char *aclname G_GNUC_UNUSED, 556 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 557 Error **errp) 558 { 559 error_setg(errp, "TLS requires GNUTLS support"); 560 return NULL; 561 } 562 563 564 void 565 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 566 { 567 } 568 569 570 int 571 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 572 Error **errp) 573 { 574 error_setg(errp, "TLS requires GNUTLS support"); 575 return -1; 576 } 577 578 579 void 580 qcrypto_tls_session_set_callbacks( 581 QCryptoTLSSession *sess G_GNUC_UNUSED, 582 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 583 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 584 void *opaque G_GNUC_UNUSED) 585 { 586 } 587 588 589 ssize_t 590 qcrypto_tls_session_write(QCryptoTLSSession *sess, 591 const char *buf, 592 size_t len) 593 { 594 errno = -EIO; 595 return -1; 596 } 597 598 599 ssize_t 600 qcrypto_tls_session_read(QCryptoTLSSession *sess, 601 char *buf, 602 size_t len) 603 { 604 errno = -EIO; 605 return -1; 606 } 607 608 609 int 610 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 611 Error **errp) 612 { 613 error_setg(errp, "TLS requires GNUTLS support"); 614 return -1; 615 } 616 617 618 QCryptoTLSSessionHandshakeStatus 619 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 620 { 621 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 622 } 623 624 625 int 626 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 627 Error **errp) 628 { 629 error_setg(errp, "TLS requires GNUTLS support"); 630 return -1; 631 } 632 633 634 char * 635 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 636 { 637 return NULL; 638 } 639 640 #endif 641