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