1
#include "source/common/tls/ssl_handshaker.h"
2

            
3
#include "envoy/stats/scope.h"
4

            
5
#include "source/common/common/assert.h"
6
#include "source/common/common/empty_string.h"
7
#include "source/common/http/headers.h"
8
#include "source/common/runtime/runtime_features.h"
9
#include "source/common/tls/context_impl.h"
10
#include "source/common/tls/utility.h"
11

            
12
using Envoy::Network::PostIoAction;
13

            
14
namespace Envoy {
15
namespace Extensions {
16
namespace TransportSockets {
17
namespace Tls {
18

            
19
1
void ValidateResultCallbackImpl::onSslHandshakeCancelled() { extended_socket_info_.reset(); }
20

            
21
void ValidateResultCallbackImpl::onCertValidationResult(bool succeeded,
22
                                                        Ssl::ClientValidationStatus detailed_status,
23
                                                        const std::string& error_details,
24
8
                                                        uint8_t tls_alert) {
25
8
  if (!extended_socket_info_.has_value()) {
26
1
    return;
27
1
  }
28
7
  extended_socket_info_->setCertificateValidationStatus(detailed_status);
29
7
  extended_socket_info_->setCertificateValidationAlert(tls_alert);
30
7
  if (!error_details.empty()) {
31
2
    extended_socket_info_->setCertificateValidationError(error_details);
32
2
  }
33
7
  extended_socket_info_->onCertificateValidationCompleted(succeeded, true);
34
7
}
35

            
36
1
void CertificateSelectionCallbackImpl::onSslHandshakeCancelled() { extended_socket_info_.reset(); }
37

            
38
void CertificateSelectionCallbackImpl::onCertificateSelectionResult(
39
31
    OptRef<const Ssl::TlsContext> selected_ctx, bool staple) {
40
31
  if (!extended_socket_info_.has_value()) {
41
    return;
42
  }
43
31
  extended_socket_info_->onCertificateSelectionCompleted(selected_ctx, staple, true);
44
31
}
45

            
46
2591
SslExtendedSocketInfoImpl::~SslExtendedSocketInfoImpl() {
47
2591
  if (cert_validate_result_callback_.has_value()) {
48
1
    cert_validate_result_callback_->onSslHandshakeCancelled();
49
1
  }
50
2591
  if (cert_selection_callback_.has_value()) {
51
1
    cert_selection_callback_->onSslHandshakeCancelled();
52
1
  }
53
2591
}
54

            
55
void SslExtendedSocketInfoImpl::setCertificateValidationStatus(
56
1193
    Envoy::Ssl::ClientValidationStatus validated) {
57
1193
  certificate_validation_status_ = validated;
58
1193
}
59

            
60
2
Envoy::Ssl::ClientValidationStatus SslExtendedSocketInfoImpl::certificateValidationStatus() const {
61
2
  return certificate_validation_status_;
62
2
}
63

            
64
1193
void SslExtendedSocketInfoImpl::onCertificateValidationCompleted(bool succeeded, bool async) {
65
1193
  cert_validation_result_ =
66
1193
      succeeded ? Ssl::ValidateStatus::Successful : Ssl::ValidateStatus::Failed;
67
1193
  if (cert_validate_result_callback_.has_value()) {
68
1193
    cert_validate_result_callback_.reset();
69
    // Resume handshake.
70
1193
    if (async) {
71
7
      ssl_handshaker_.handshakeCallbacks()->onAsynchronousCertValidationComplete();
72
7
    }
73
1193
  }
74
1193
}
75

            
76
1194
Ssl::ValidateResultCallbackPtr SslExtendedSocketInfoImpl::createValidateResultCallback() {
77
1194
  auto callback = std::make_unique<ValidateResultCallbackImpl>(
78
1194
      ssl_handshaker_.handshakeCallbacks()->connection().dispatcher(), *this);
79
1194
  cert_validate_result_callback_ = *callback;
80
1194
  cert_validation_result_ = Ssl::ValidateStatus::Pending;
81
1194
  return callback;
82
1194
}
83

            
84
void SslExtendedSocketInfoImpl::onCertificateSelectionCompleted(
85
1293
    OptRef<const Ssl::TlsContext> selected_ctx, bool staple, bool async) {
86
1293
  RELEASE_ASSERT(cert_selection_result_ == Ssl::CertificateSelectionStatus::Pending,
87
1293
                 "onCertificateSelectionCompleted twice");
88
1293
  if (!selected_ctx.has_value()) {
89
8
    cert_selection_result_ = Ssl::CertificateSelectionStatus::Failed;
90
1285
  } else {
91
1285
    cert_selection_result_ = Ssl::CertificateSelectionStatus::Successful;
92
    // Apply the selected context. This must be done before OCSP stapling below
93
    // since applying the context can remove the previously-set OCSP response.
94
    // This will only return NULL if memory allocation fails.
95
1285
    RELEASE_ASSERT(SSL_set_SSL_CTX(ssl_handshaker_.ssl(), selected_ctx->ssl_ctx_.get()) != nullptr,
96
1285
                   "");
97

            
98
1285
    if (staple) {
99
      // We avoid setting the OCSP response if the client didn't request it, but doing so is safe.
100
4
      RELEASE_ASSERT(selected_ctx->ocsp_response_,
101
4
                     "OCSP response must be present under OcspStapleAction::Staple");
102
4
      const std::vector<uint8_t>& resp_bytes = selected_ctx->ocsp_response_->rawBytes();
103
4
      const int rc =
104
4
          SSL_set_ocsp_response(ssl_handshaker_.ssl(), resp_bytes.data(), resp_bytes.size());
105
4
      RELEASE_ASSERT(rc != 0, "");
106
4
    }
107
1285
  }
108
1293
  if (cert_selection_callback_.has_value()) {
109
1293
    cert_selection_callback_.reset();
110
    // Resume handshake.
111
1293
    if (async) {
112
31
      ssl_handshaker_.handshakeCallbacks()->onAsynchronousCertificateSelectionComplete();
113
31
    }
114
1293
  }
115
1293
}
116

            
117
Ssl::CertificateSelectionCallbackPtr
118
1294
SslExtendedSocketInfoImpl::createCertificateSelectionCallback() {
119
1294
  auto callback = std::make_unique<CertificateSelectionCallbackImpl>(
120
1294
      ssl_handshaker_.handshakeCallbacks()->connection().dispatcher(), *this);
121
1294
  cert_selection_callback_ = *callback;
122
1294
  cert_selection_result_ = Ssl::CertificateSelectionStatus::Pending;
123
1294
  return callback;
124
1294
}
125

            
126
SslHandshakerImpl::SslHandshakerImpl(bssl::UniquePtr<SSL> ssl, int ssl_extended_socket_info_index,
127
                                     Ssl::HandshakeCallbacks* handshake_callbacks)
128
2591
    : ssl_(std::move(ssl)), handshake_callbacks_(handshake_callbacks),
129
2591
      extended_socket_info_(*this) {
130
2591
  SSL_set_ex_data(ssl_.get(), ssl_extended_socket_info_index, &(this->extended_socket_info_));
131
2591
}
132

            
133
bool SslHandshakerImpl::peerCertificateValidated() const {
134
  return extended_socket_info_.certificateValidationStatus() ==
135
         Envoy::Ssl::ClientValidationStatus::Validated;
136
}
137

            
138
7342
Network::PostIoAction SslHandshakerImpl::doHandshake() {
139
7342
  ASSERT(state_ != Ssl::SocketState::HandshakeComplete && state_ != Ssl::SocketState::ShutdownSent);
140
7342
  int rc = SSL_do_handshake(ssl());
141
7342
  if (rc == 1) {
142
2128
    state_ = Ssl::SocketState::HandshakeComplete;
143
2128
    handshake_callbacks_->onSuccess(ssl());
144

            
145
    // It's possible that we closed during the handshake callback.
146
2128
    return handshake_callbacks_->connection().state() == Network::Connection::State::Open
147
2128
               ? PostIoAction::KeepOpen
148
2128
               : PostIoAction::Close;
149
5214
  } else {
150
5214
    int err = SSL_get_error(ssl(), rc);
151
5214
    ENVOY_CONN_LOG(trace, "ssl error occurred while read: {}", handshake_callbacks_->connection(),
152
5214
                   Utility::getErrorDescription(err));
153
5214
    switch (err) {
154
4819
    case SSL_ERROR_WANT_READ:
155
4833
    case SSL_ERROR_WANT_WRITE:
156
4833
      return PostIoAction::KeepOpen;
157
47
    case SSL_ERROR_PENDING_CERTIFICATE:
158
65
    case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
159
83
    case SSL_ERROR_WANT_CERTIFICATE_VERIFY:
160
107
    case SSL_ERROR_WANT_X509_LOOKUP:
161
107
      state_ = Ssl::SocketState::HandshakeInProgress;
162
107
      return PostIoAction::KeepOpen;
163
274
    default:
164
274
      handshake_callbacks_->onFailure();
165
274
      return PostIoAction::Close;
166
5214
    }
167
5214
  }
168
7342
}
169

            
170
} // namespace Tls
171
} // namespace TransportSockets
172
} // namespace Extensions
173
} // namespace Envoy