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 return 0; 355 } else if (object_dynamic_cast(OBJECT(session->creds), 356 TYPE_QCRYPTO_TLS_CREDS_X509)) { 357 if (session->creds->verifyPeer) { 358 return qcrypto_tls_session_check_certificate(session, 359 errp); 360 } else { 361 return 0; 362 } 363 } else { 364 error_setg(errp, "Unexpected credential type %s", 365 object_get_typename(OBJECT(session->creds))); 366 return -1; 367 } 368 } 369 370 371 void 372 qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, 373 QCryptoTLSSessionWriteFunc writeFunc, 374 QCryptoTLSSessionReadFunc readFunc, 375 void *opaque) 376 { 377 session->writeFunc = writeFunc; 378 session->readFunc = readFunc; 379 session->opaque = opaque; 380 } 381 382 383 ssize_t 384 qcrypto_tls_session_write(QCryptoTLSSession *session, 385 const char *buf, 386 size_t len) 387 { 388 ssize_t ret = gnutls_record_send(session->handle, buf, len); 389 390 if (ret < 0) { 391 switch (ret) { 392 case GNUTLS_E_AGAIN: 393 errno = EAGAIN; 394 break; 395 case GNUTLS_E_INTERRUPTED: 396 errno = EINTR; 397 break; 398 default: 399 errno = EIO; 400 break; 401 } 402 ret = -1; 403 } 404 405 return ret; 406 } 407 408 409 ssize_t 410 qcrypto_tls_session_read(QCryptoTLSSession *session, 411 char *buf, 412 size_t len) 413 { 414 ssize_t ret = gnutls_record_recv(session->handle, buf, len); 415 416 if (ret < 0) { 417 switch (ret) { 418 case GNUTLS_E_AGAIN: 419 errno = EAGAIN; 420 break; 421 case GNUTLS_E_INTERRUPTED: 422 errno = EINTR; 423 break; 424 default: 425 errno = EIO; 426 break; 427 } 428 ret = -1; 429 } 430 431 return ret; 432 } 433 434 435 int 436 qcrypto_tls_session_handshake(QCryptoTLSSession *session, 437 Error **errp) 438 { 439 int ret = gnutls_handshake(session->handle); 440 if (ret == 0) { 441 session->handshakeComplete = true; 442 } else { 443 if (ret == GNUTLS_E_INTERRUPTED || 444 ret == GNUTLS_E_AGAIN) { 445 ret = 1; 446 } else { 447 error_setg(errp, "TLS handshake failed: %s", 448 gnutls_strerror(ret)); 449 ret = -1; 450 } 451 } 452 453 return ret; 454 } 455 456 457 QCryptoTLSSessionHandshakeStatus 458 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) 459 { 460 if (session->handshakeComplete) { 461 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 462 } else if (gnutls_record_get_direction(session->handle) == 0) { 463 return QCRYPTO_TLS_HANDSHAKE_RECVING; 464 } else { 465 return QCRYPTO_TLS_HANDSHAKE_SENDING; 466 } 467 } 468 469 470 int 471 qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, 472 Error **errp) 473 { 474 gnutls_cipher_algorithm_t cipher; 475 int ssf; 476 477 cipher = gnutls_cipher_get(session->handle); 478 ssf = gnutls_cipher_get_key_size(cipher); 479 if (!ssf) { 480 error_setg(errp, "Cannot get TLS cipher key size"); 481 return -1; 482 } 483 return ssf; 484 } 485 486 487 char * 488 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) 489 { 490 if (session->peername) { 491 return g_strdup(session->peername); 492 } 493 return NULL; 494 } 495 496 497 #else /* ! CONFIG_GNUTLS */ 498 499 500 QCryptoTLSSession * 501 qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, 502 const char *hostname G_GNUC_UNUSED, 503 const char *aclname G_GNUC_UNUSED, 504 QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, 505 Error **errp) 506 { 507 error_setg(errp, "TLS requires GNUTLS support"); 508 return NULL; 509 } 510 511 512 void 513 qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) 514 { 515 } 516 517 518 int 519 qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, 520 Error **errp) 521 { 522 error_setg(errp, "TLS requires GNUTLS support"); 523 return -1; 524 } 525 526 527 void 528 qcrypto_tls_session_set_callbacks( 529 QCryptoTLSSession *sess G_GNUC_UNUSED, 530 QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, 531 QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, 532 void *opaque G_GNUC_UNUSED) 533 { 534 } 535 536 537 ssize_t 538 qcrypto_tls_session_write(QCryptoTLSSession *sess, 539 const char *buf, 540 size_t len) 541 { 542 errno = -EIO; 543 return -1; 544 } 545 546 547 ssize_t 548 qcrypto_tls_session_read(QCryptoTLSSession *sess, 549 char *buf, 550 size_t len) 551 { 552 errno = -EIO; 553 return -1; 554 } 555 556 557 int 558 qcrypto_tls_session_handshake(QCryptoTLSSession *sess, 559 Error **errp) 560 { 561 error_setg(errp, "TLS requires GNUTLS support"); 562 return -1; 563 } 564 565 566 QCryptoTLSSessionHandshakeStatus 567 qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) 568 { 569 return QCRYPTO_TLS_HANDSHAKE_COMPLETE; 570 } 571 572 573 int 574 qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, 575 Error **errp) 576 { 577 error_setg(errp, "TLS requires GNUTLS support"); 578 return -1; 579 } 580 581 582 char * 583 qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) 584 { 585 return NULL; 586 } 587 588 #endif 589