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