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