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

            
3
#include "source/common/tls/utility.h"
4

            
5
namespace Envoy {
6
namespace Extensions {
7
namespace TransportSockets {
8
namespace Tls {
9

            
10
DefaultTlsCertificateSelector::DefaultTlsCertificateSelector(
11
    const Ssl::ServerContextConfig& config, Ssl::TlsCertificateSelectorContext& selector_ctx)
12
3452
    : server_ctx_(dynamic_cast<ServerContextImpl&>(selector_ctx)),
13
3452
      tls_contexts_(selector_ctx.getTlsContexts()), ocsp_staple_policy_(config.ocspStaplePolicy()),
14
3452
      full_scan_certs_on_sni_mismatch_(config.fullScanCertsOnSNIMismatch()) {
15
3526
  for (auto& ctx : tls_contexts_) {
16
3526
    if (ctx.cert_chain_ == nullptr) {
17
2
      continue;
18
2
    }
19
3524
    bssl::UniquePtr<EVP_PKEY> public_key(X509_get_pubkey(ctx.cert_chain_.get()));
20
3524
    const int pkey_id = EVP_PKEY_id(public_key.get());
21
    // Load DNS SAN entries and Subject Common Name as server name patterns after certificate
22
    // chain loaded, and populate ServerNamesMap which will be used to match SNI.
23
3524
    has_rsa_ |= (pkey_id == EVP_PKEY_RSA);
24
3524
    populateServerNamesMap(ctx, pkey_id);
25
3524
  }
26
3452
};
27

            
28
void DefaultTlsCertificateSelector::populateServerNamesMap(const Ssl::TlsContext& ctx,
29
3524
                                                           int pkey_id) {
30
3524
  if (ctx.cert_chain_ == nullptr) {
31
    return;
32
  }
33

            
34
4943
  auto populate = [&](const std::string& sn) {
35
4923
    std::string sn_pattern = sn;
36
4923
    if (absl::StartsWith(sn, "*.")) {
37
1691
      sn_pattern = sn.substr(1);
38
1691
    }
39
4923
    PkeyTypesMap pkey_types_map;
40
    // Multiple certs with different key type are allowed for one server name pattern.
41
4923
    auto sn_match = server_names_map_.try_emplace(sn_pattern, pkey_types_map).first;
42
4923
    auto pt_match = sn_match->second.find(pkey_id);
43
4923
    if (pt_match != sn_match->second.end()) {
44
      // When there are duplicate names, prefer the earlier one.
45
      //
46
      // If all of the SANs in a certificate are unused due to duplicates, it could be useful
47
      // to issue a warning, but that would require additional tracking that hasn't been
48
      // implemented.
49
5
      return;
50
5
    }
51
4918
    sn_match->second.emplace(
52
4918
        std::pair<int, std::reference_wrapper<const Ssl::TlsContext>>(pkey_id, ctx));
53
4918
  };
54

            
55
3524
  bssl::UniquePtr<GENERAL_NAMES> san_names(static_cast<GENERAL_NAMES*>(
56
3524
      X509_get_ext_d2i(ctx.cert_chain_.get(), NID_subject_alt_name, nullptr, nullptr)));
57
3524
  if (san_names != nullptr) {
58
3385
    auto dns_sans = Utility::getSubjectAltNames(*ctx.cert_chain_, GEN_DNS);
59
    // https://www.rfc-editor.org/rfc/rfc6066#section-3
60
    // Currently, the only server names supported are DNS hostnames, so we
61
    // only save dns san entries to match SNI.
62
4805
    for (const auto& san : dns_sans) {
63
4785
      populate(san);
64
4785
    }
65
3395
  } else {
66
    // https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4
67
    // As noted, a client MUST NOT seek a match for a reference identifier
68
    // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID,
69
    // URI-ID, or any application-specific identifier types supported by the
70
    // client.
71
139
    const X509_NAME* cert_subject = X509_get_subject_name(ctx.cert_chain_.get());
72
139
    const int cn_index = X509_NAME_get_index_by_NID(cert_subject, NID_commonName, -1);
73
139
    if (cn_index >= 0) {
74
138
      const X509_NAME_ENTRY* cn_entry = X509_NAME_get_entry(cert_subject, cn_index);
75
138
      if (cn_entry) {
76
138
        const ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
77
138
        if (ASN1_STRING_length(cn_asn1) > 0) {
78
138
          std::string subject_cn(reinterpret_cast<const char*>(ASN1_STRING_get0_data(cn_asn1)),
79
138
                                 ASN1_STRING_length(cn_asn1));
80
138
          populate(subject_cn);
81
138
        }
82
138
      }
83
138
    }
84
139
  }
85
3524
}
86

            
87
Ssl::SelectionResult
88
DefaultTlsCertificateSelector::selectTlsContext(const SSL_CLIENT_HELLO& ssl_client_hello,
89
1248
                                                Ssl::CertificateSelectionCallbackPtr) {
90
1248
  absl::string_view sni =
91
1248
      absl::NullSafeStringView(SSL_get_servername(ssl_client_hello.ssl, TLSEXT_NAMETYPE_host_name));
92
1248
  const Ssl::CurveNIDVector client_ecdsa_capabilities =
93
1248
      server_ctx_.getClientEcdsaCapabilities(ssl_client_hello);
94
1248
  const bool client_ocsp_capable = isClientOcspCapable(ssl_client_hello);
95

            
96
1248
  auto [selected_ctx, ocsp_staple_action] =
97
1248
      findTlsContext(sni, client_ecdsa_capabilities, client_ocsp_capable, nullptr);
98

            
99
1248
  auto stats = server_ctx_.stats();
100
1248
  if (client_ocsp_capable) {
101
10
    stats.ocsp_staple_requests_.inc();
102
10
  }
103

            
104
1248
  switch (ocsp_staple_action) {
105
4
  case Ssl::OcspStapleAction::Staple:
106
4
    stats.ocsp_staple_responses_.inc();
107
4
    break;
108
2
  case Ssl::OcspStapleAction::NoStaple:
109
2
    stats.ocsp_staple_omitted_.inc();
110
2
    break;
111
4
  case Ssl::OcspStapleAction::Fail:
112
4
    stats.ocsp_staple_failed_.inc();
113
4
    return {Ssl::SelectionResult::SelectionStatus::Failed, nullptr, false};
114
1238
  case Ssl::OcspStapleAction::ClientNotCapable:
115
    // This happens when client does not support OCSP, do nothing.
116
1238
    break;
117
1248
  }
118

            
119
1244
  return {Ssl::SelectionResult::SelectionStatus::Success, &selected_ctx,
120
1244
          ocsp_staple_action == Ssl::OcspStapleAction::Staple};
121
1248
}
122

            
123
Ssl::OcspStapleAction
124
ocspStapleAction(const Ssl::TlsContext& ctx, bool client_ocsp_capable,
125
6052
                 Ssl::ServerContextConfig::OcspStaplePolicy ocsp_staple_policy) {
126
6052
  if (!client_ocsp_capable) {
127
6035
    return Ssl::OcspStapleAction::ClientNotCapable;
128
6035
  }
129

            
130
17
  auto& response = ctx.ocsp_response_;
131

            
132
17
  auto policy = ocsp_staple_policy;
133
17
  if (ctx.is_must_staple_) {
134
    // The certificate has the must-staple extension, so upgrade the policy to match.
135
4
    policy = Ssl::ServerContextConfig::OcspStaplePolicy::MustStaple;
136
4
  }
137

            
138
17
  const bool valid_response = response && !response->isExpired();
139

            
140
17
  switch (policy) {
141
6
  case Ssl::ServerContextConfig::OcspStaplePolicy::LenientStapling:
142
6
    if (!valid_response) {
143
3
      return Ssl::OcspStapleAction::NoStaple;
144
3
    }
145
3
    return Ssl::OcspStapleAction::Staple;
146

            
147
3
  case Ssl::ServerContextConfig::OcspStaplePolicy::StrictStapling:
148
3
    if (valid_response) {
149
      return Ssl::OcspStapleAction::Staple;
150
    }
151
3
    if (response) {
152
      // Expired response.
153
2
      return Ssl::OcspStapleAction::Fail;
154
2
    }
155
1
    return Ssl::OcspStapleAction::NoStaple;
156

            
157
8
  case Ssl::ServerContextConfig::OcspStaplePolicy::MustStaple:
158
8
    if (!valid_response) {
159
7
      return Ssl::OcspStapleAction::Fail;
160
7
    }
161
1
    return Ssl::OcspStapleAction::Staple;
162
17
  }
163
  PANIC_DUE_TO_CORRUPT_ENUM;
164
}
165

            
166
std::pair<const Ssl::TlsContext&, Ssl::OcspStapleAction>
167
DefaultTlsCertificateSelector::findTlsContext(absl::string_view sni,
168
                                              const Ssl::CurveNIDVector& client_ecdsa_capabilities,
169
5975
                                              bool client_ocsp_capable, bool* cert_matched_sni) {
170
5975
  bool unused = false;
171
5975
  if (cert_matched_sni == nullptr) {
172
    // Avoid need for nullptr checks when this is set.
173
3063
    cert_matched_sni = &unused;
174
3063
  }
175

            
176
  // selected_ctx represents the final selected certificate, it should meet all requirements or pick
177
  // a candidate.
178
5975
  const Ssl::TlsContext* selected_ctx = nullptr;
179
5975
  const Ssl::TlsContext* candidate_ctx = nullptr;
180
5975
  Ssl::OcspStapleAction ocsp_staple_action;
181

            
182
  // If the capabilities list vector is not empty, then the client is ECDSA-capable.
183
5975
  const bool client_ecdsa_capable = !client_ecdsa_capabilities.empty();
184

            
185
6005
  auto selected = [&](const Ssl::TlsContext& ctx) -> bool {
186
5957
    auto action = ocspStapleAction(ctx, client_ocsp_capable, ocsp_staple_policy_);
187
5957
    if (action == Ssl::OcspStapleAction::Fail) {
188
      // The selected ctx must adhere to OCSP policy
189
5
      return false;
190
5
    }
191
    // If the client is ECDSA-capable and the context is ECDSA, we check if it is capable of
192
    // handling the curves in the cert in a given TlsContext. If the client is not ECDSA-capable,
193
    // we will not std::find anything here and move on. If the context is RSA, we will not find
194
    // the value of `EC_CURVE_INVALID_NID` in an vector of ECDSA capabilities.
195
    // If we have a matching curve NID in our client capabilities, return `true`.
196
5952
    if (std::find(client_ecdsa_capabilities.begin(), client_ecdsa_capabilities.end(),
197
5952
                  ctx.ec_group_curve_name_) != client_ecdsa_capabilities.end()) {
198
49
      selected_ctx = &ctx;
199
49
      ocsp_staple_action = action;
200
49
      return true;
201
49
    }
202

            
203
    // If the client is not ECDSA-capable and the `ctx` is non-ECDSA, then select this `ctx`.
204
5903
    if (!client_ecdsa_capable && ctx.ec_group_curve_name_ == Ssl::EC_CURVE_INVALID_NID) {
205
59
      selected_ctx = &ctx;
206
59
      ocsp_staple_action = action;
207
59
      return true;
208
59
    }
209

            
210
5844
    if (client_ecdsa_capable && ctx.ec_group_curve_name_ == Ssl::EC_CURVE_INVALID_NID &&
211
5844
        candidate_ctx == nullptr) {
212
      // ECDSA cert is preferred if client is ECDSA capable, so RSA cert is marked as a candidate,
213
      // searching will continue until exhausting all certs or find a exact match.
214
5834
      candidate_ctx = &ctx;
215
5834
      ocsp_staple_action = action;
216
5834
      return false;
217
5834
    }
218

            
219
10
    return false;
220
5844
  };
221

            
222
7903
  auto select_from_map = [this, &selected](absl::string_view server_name) -> void {
223
6921
    auto it = server_names_map_.find(server_name);
224
6921
    if (it == server_names_map_.end()) {
225
2007
      return;
226
2007
    }
227
4914
    const auto& pkey_types_map = it->second;
228
4922
    for (const auto& entry : pkey_types_map) {
229
4922
      if (selected(entry.second.get())) {
230
22
        break;
231
22
      }
232
4922
    }
233
4914
  };
234

            
235
10943
  auto tail_select = [&](bool go_to_next_phase) {
236
10943
    if (selected_ctx == nullptr) {
237
7854
      selected_ctx = candidate_ctx;
238
7854
    }
239

            
240
10943
    if (selected_ctx == nullptr && !go_to_next_phase) {
241
63
      selected_ctx = &tls_contexts_[0];
242
63
      ocsp_staple_action =
243
63
          ocspStapleAction(*selected_ctx, client_ocsp_capable, ocsp_staple_policy_);
244
63
    }
245
10943
  };
246

            
247
  // Select cert based on SNI if SNI is provided by client.
248
5975
  if (!sni.empty()) {
249
    // Match on exact server name, i.e. "www.example.com" for "www.example.com".
250
4967
    select_from_map(sni);
251
4967
    tail_select(true);
252

            
253
4967
    if (selected_ctx == nullptr) {
254
      // Match on wildcard domain, i.e. ".example.com" for "www.example.com".
255
      // https://datatracker.ietf.org/doc/html/rfc6125#section-6.4
256
1986
      size_t pos = sni.find('.', 1);
257
1986
      if (pos < sni.size() - 1 && pos != std::string::npos) {
258
1954
        absl::string_view wildcard = sni.substr(pos);
259
1954
        select_from_map(wildcard);
260
1954
      }
261
1986
    }
262
4967
    *cert_matched_sni = (selected_ctx != nullptr || candidate_ctx != nullptr);
263
    // tail_select(full_scan_certs_on_sni_mismatch_);
264
4967
    tail_select(full_scan_certs_on_sni_mismatch_);
265
4967
  }
266
  // Full scan certs if SNI is not provided by client;
267
  // Full scan certs if client provides SNI but no cert matches to it,
268
  // it requires full_scan_certs_on_sni_mismatch is enabled.
269
5975
  if (selected_ctx == nullptr) {
270
1009
    candidate_ctx = nullptr;
271
    // Skip loop when there is no cert compatible to key type
272
1009
    if (client_ecdsa_capable || (!client_ecdsa_capable && has_rsa_)) {
273
1035
      for (const auto& ctx : tls_contexts_) {
274
1035
        if (selected(ctx)) {
275
86
          break;
276
86
        }
277
1035
      }
278
1003
    }
279
1009
    tail_select(false);
280
1009
  }
281

            
282
5975
  ASSERT(selected_ctx != nullptr);
283
5975
  return {*selected_ctx, ocsp_staple_action};
284
5975
}
285

            
286
} // namespace Tls
287
} // namespace TransportSockets
288
} // namespace Extensions
289
} // namespace Envoy