|
23 | 23 | #include <openssl/bio.h>
|
24 | 24 | #include <openssl/ssl.h>
|
25 | 25 | #include <openssl/err.h>
|
| 26 | +#include <openssl/ocsp.h> |
26 | 27 | #include <openssl/x509v3.h>
|
27 | 28 | #include <openssl/crypto.h>
|
28 | 29 |
|
29 | 30 | #include <string.h>
|
30 | 31 |
|
31 | 32 | #include "mongoc-init.h"
|
| 33 | +#include "mongoc-openssl-private.h" |
32 | 34 | #include "mongoc-socket.h"
|
33 | 35 | #include "mongoc-ssl.h"
|
34 |
| -#include "mongoc-openssl-private.h" |
35 |
| -#include "mongoc-trace-private.h" |
| 36 | +#include "mongoc-stream-tls-openssl-private.h" |
36 | 37 | #include "mongoc-thread-private.h"
|
| 38 | +#include "mongoc-trace-private.h" |
37 | 39 | #include "mongoc-util-private.h"
|
38 | 40 |
|
39 | 41 | #ifdef _WIN32
|
@@ -451,6 +453,189 @@ _mongoc_openssl_setup_pem_file (SSL_CTX *ctx,
|
451 | 453 | return 1;
|
452 | 454 | }
|
453 | 455 |
|
| 456 | +#ifdef MONGOC_ENABLE_OCSP |
| 457 | + |
| 458 | +static X509 * |
| 459 | +_get_issuer (X509 *cert, STACK_OF (X509) * chain) |
| 460 | +{ |
| 461 | + X509 *issuer = NULL, *candidate = NULL; |
| 462 | + X509_NAME *issuer_name = NULL, *candidate_name = NULL; |
| 463 | + int i; |
| 464 | + |
| 465 | + issuer_name = X509_get_issuer_name (cert); |
| 466 | + for (i = 0; i < sk_X509_num (chain) && issuer == NULL; i++) { |
| 467 | + candidate = sk_X509_value (chain, i); |
| 468 | + candidate_name = X509_get_subject_name (candidate); |
| 469 | + if (0 == X509_NAME_cmp (candidate_name, issuer_name)) { |
| 470 | + issuer = candidate; |
| 471 | + } |
| 472 | + } |
| 473 | + return issuer; |
| 474 | +} |
| 475 | + |
| 476 | +#define ERR_STR (ERR_error_string (ERR_get_error (), NULL)) |
| 477 | + |
| 478 | +int |
| 479 | +_mongoc_ocsp_tlsext_status_cb (SSL *ssl, void *arg) |
| 480 | +{ |
| 481 | + const int ERROR = -1, FAILURE = 0, SUCCESS = 1; |
| 482 | + OCSP_RESPONSE *resp = NULL; |
| 483 | + OCSP_BASICRESP *basic = NULL; |
| 484 | + X509_STORE *store = NULL; |
| 485 | + X509 *peer = NULL, *issuer = NULL; |
| 486 | + STACK_OF (X509) *cert_chain = NULL; |
| 487 | + const unsigned char *r = NULL; |
| 488 | + int cert_status, reason, len, status, ret; |
| 489 | + OCSP_CERTID *id = NULL; |
| 490 | + mongoc_openssl_ocsp_opt_t *opts = (mongoc_openssl_ocsp_opt_t *) arg; |
| 491 | + ASN1_GENERALIZEDTIME *produced_at = NULL, *this_update = NULL, |
| 492 | + *next_update = NULL; |
| 493 | + |
| 494 | + if (opts->weak_cert_validation) { |
| 495 | + return SUCCESS; |
| 496 | + } |
| 497 | + |
| 498 | + if (!(peer = SSL_get_peer_certificate (ssl))) { |
| 499 | + MONGOC_ERROR ("No certificate was presented by the peer: %s", ERR_STR); |
| 500 | + ret = ERROR; |
| 501 | + goto done; |
| 502 | + } |
| 503 | + |
| 504 | + /* Get the stapled OCSP response returned by the server */ |
| 505 | + len = SSL_get_tlsext_status_ocsp_resp (ssl, &r); |
| 506 | + if (!r) { |
| 507 | + bool must_staple = X509_get_ext_d2i (peer, NID_tlsfeature, 0, 0) != NULL; |
| 508 | + if (must_staple) { |
| 509 | + MONGOC_ERROR ("Server must contain a stapled response: %s", ERR_STR); |
| 510 | + ret = FAILURE; |
| 511 | + goto done; |
| 512 | + } |
| 513 | + ret = SUCCESS; // TODO: contact OCSP responder |
| 514 | + goto done; |
| 515 | + } |
| 516 | + |
| 517 | + /* obtain an OCSP_RESPONSE object from the OCSP response */ |
| 518 | + if (!d2i_OCSP_RESPONSE (&resp, &r, len)) { |
| 519 | + MONGOC_ERROR ("Failed to parse OCSP response: %s", ERR_STR); |
| 520 | + ret = ERROR; |
| 521 | + goto done; |
| 522 | + } |
| 523 | + |
| 524 | + /* Validate the OCSP response status of the OCSP_RESPONSE object */ |
| 525 | + status = OCSP_response_status (resp); |
| 526 | + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { |
| 527 | + MONGOC_ERROR ("OCSP response error %d %s", |
| 528 | + status, |
| 529 | + OCSP_response_status_str (status)); |
| 530 | + ret = ERROR; |
| 531 | + goto done; |
| 532 | + } |
| 533 | + |
| 534 | + /* Get the OCSP_BASICRESP structure contained in OCSP_RESPONSE object for the |
| 535 | + * peer cert */ |
| 536 | + basic = OCSP_response_get1_basic (resp); |
| 537 | + if (!basic) { |
| 538 | + MONGOC_ERROR ("Could not find BasicOCSPResponse: %s", ERR_STR); |
| 539 | + ret = ERROR; |
| 540 | + goto done; |
| 541 | + } |
| 542 | + |
| 543 | + store = SSL_CTX_get_cert_store (SSL_get_SSL_CTX (ssl)); |
| 544 | + |
| 545 | + /* Get a STACK_OF(X509) certs forming the cert chain of the peer, including |
| 546 | + * the peer's cert */ |
| 547 | + if (!(cert_chain = SSL_get0_verified_chain (ssl))) { |
| 548 | + MONGOC_ERROR ("No certificate was presented by the peer: %s", ERR_STR); |
| 549 | + ret = ERROR; |
| 550 | + goto done; |
| 551 | + } |
| 552 | + |
| 553 | + /* |
| 554 | + * checks that the basic response message is correctly signed and that the |
| 555 | + * signer certificate can be validated. |
| 556 | + * 1. The function first verifies the signer cert of the response is in the |
| 557 | + * given cert chain. |
| 558 | + * 2. Next, the function verifies the signature of the basic response. |
| 559 | + * 3. Finally, the function validates the signer cert, constructing the |
| 560 | + * validation path via the untrusted cert chain. |
| 561 | + */ |
| 562 | + if (SUCCESS != OCSP_basic_verify (basic, cert_chain, store, 0)) { |
| 563 | + MONGOC_ERROR ("OCSP response failed verification: %s", ERR_STR); |
| 564 | + ret = ERROR; |
| 565 | + goto done; |
| 566 | + } |
| 567 | + |
| 568 | + if (!(issuer = _get_issuer (peer, cert_chain))) { |
| 569 | + MONGOC_ERROR ("Could not get issuer from peer cert"); |
| 570 | + ret = ERROR; |
| 571 | + goto done; |
| 572 | + } |
| 573 | + |
| 574 | + if (!(id = OCSP_cert_to_id (NULL /* SHA1 */, peer, issuer))) { |
| 575 | + MONGOC_ERROR ("Could not obtain a valid OCSP_CERTID for peer: %s", |
| 576 | + ERR_STR); |
| 577 | + ret = ERROR; |
| 578 | + goto done; |
| 579 | + } |
| 580 | + |
| 581 | + /* searches the basic response for an OCSP response for the given cert ID */ |
| 582 | + if (!OCSP_resp_find_status (basic, |
| 583 | + id, |
| 584 | + &cert_status, |
| 585 | + &reason, |
| 586 | + &produced_at, |
| 587 | + &this_update, |
| 588 | + &next_update)) { |
| 589 | + MONGOC_ERROR ("No OCSP response found for the peer certificate: %s", |
| 590 | + ERR_STR); |
| 591 | + ret = ERROR; |
| 592 | + goto done; |
| 593 | + } |
| 594 | + |
| 595 | + /* checks the validity of this_update and next_update values */ |
| 596 | + if (!OCSP_check_validity (this_update, next_update, 0L, -1L)) { |
| 597 | + MONGOC_ERROR ("OCSP response has expired: %s", ERR_STR); |
| 598 | + ret = ERROR; |
| 599 | + goto done; |
| 600 | + } |
| 601 | + |
| 602 | + switch (cert_status) { |
| 603 | + case V_OCSP_CERTSTATUS_GOOD: |
| 604 | + /* TODO: cache response */ |
| 605 | + break; |
| 606 | + |
| 607 | + case V_OCSP_CERTSTATUS_REVOKED: |
| 608 | + MONGOC_ERROR ("OCSP Certificate Status: Revoked. Reason %d", reason); |
| 609 | + ret = FAILURE; |
| 610 | + goto done; |
| 611 | + |
| 612 | + default: /* V_OCSP_CERTSTATUS_UNKNOWN */ |
| 613 | + break; |
| 614 | + } |
| 615 | + |
| 616 | + /* Validate hostname matches cert */ |
| 617 | + if (!opts->allow_invalid_hostname && |
| 618 | + X509_check_host (peer, opts->host, 0, 0, NULL) != SUCCESS && |
| 619 | + X509_check_ip_asc (peer, opts->host, 0) != SUCCESS) { |
| 620 | + ret = FAILURE; |
| 621 | + goto done; |
| 622 | + } |
| 623 | + |
| 624 | + ret = SUCCESS; |
| 625 | +done: |
| 626 | + if (basic) |
| 627 | + OCSP_BASICRESP_free (basic); |
| 628 | + if (resp) |
| 629 | + OCSP_RESPONSE_free (resp); |
| 630 | + if (id) |
| 631 | + OCSP_CERTID_free (id); |
| 632 | + if (peer) |
| 633 | + X509_free (peer); |
| 634 | + if (issuer) |
| 635 | + X509_free (issuer); |
| 636 | + return ret; |
| 637 | +} |
| 638 | +#endif |
454 | 639 |
|
455 | 640 | /**
|
456 | 641 | * _mongoc_openssl_ctx_new:
|
@@ -493,7 +678,8 @@ _mongoc_openssl_ctx_new (mongoc_ssl_opt_t *opt)
|
493 | 678 | ssl_ctx_options |= SSL_OP_NO_COMPRESSION;
|
494 | 679 | #endif
|
495 | 680 |
|
496 |
| -/* man SSL_get_options says: "SSL_OP_NO_RENEGOTIATION options were added in OpenSSL 1.1.1". */ |
| 681 | +/* man SSL_get_options says: "SSL_OP_NO_RENEGOTIATION options were added in |
| 682 | + * OpenSSL 1.1.1". */ |
497 | 683 | #ifdef SSL_OP_NO_RENEGOTIATION
|
498 | 684 | ssl_ctx_options |= SSL_OP_NO_RENEGOTIATION;
|
499 | 685 | #endif
|
|
0 commit comments