1
#include "source/common/ssl/certificate_validation_context_config_impl.h"
2

            
3
#include "envoy/common/exception.h"
4
#include "envoy/config/core/v3/extension.pb.h"
5
#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h"
6
#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h"
7

            
8
#include "source/common/common/empty_string.h"
9
#include "source/common/common/fmt.h"
10
#include "source/common/common/logger.h"
11
#include "source/common/config/datasource.h"
12
#include "source/common/runtime/runtime_features.h"
13

            
14
#include "spdlog/spdlog.h"
15

            
16
namespace Envoy {
17
namespace Ssl {
18

            
19
static const std::string INLINE_STRING = "<inline>";
20

            
21
CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl(
22
    std::string ca_cert, std::string certificate_revocation_list,
23
    const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config,
24
    bool auto_sni_san_match, Api::Api& api, const std::string& ca_cert_name)
25
5993
    : ca_cert_(ca_cert),
26
5993
      ca_cert_path_(Config::DataSource::getPath(config.trusted_ca())
27
5993
                        .value_or(ca_cert_.empty() ? EMPTY_STRING : INLINE_STRING)),
28
5993
      ca_cert_name_(ca_cert_name), certificate_revocation_list_(certificate_revocation_list),
29
      certificate_revocation_list_path_(
30
5993
          Config::DataSource::getPath(config.crl())
31
5993
              .value_or(certificate_revocation_list_.empty() ? EMPTY_STRING : INLINE_STRING)),
32
5993
      subject_alt_name_matchers_(getSubjectAltNameMatchers(config)),
33
5993
      verify_certificate_hash_list_(config.verify_certificate_hash().begin(),
34
5993
                                    config.verify_certificate_hash().end()),
35
5993
      verify_certificate_spki_list_(config.verify_certificate_spki().begin(),
36
5993
                                    config.verify_certificate_spki().end()),
37
5993
      allow_expired_certificate_(config.allow_expired_certificate()),
38
5993
      trust_chain_verification_(config.trust_chain_verification()),
39
      custom_validator_config_(
40
5993
          config.has_custom_validator_config()
41
5993
              ? absl::make_optional<envoy::config::core::v3::TypedExtensionConfig>(
42
37
                    config.custom_validator_config())
43
5993
              : absl::nullopt),
44
5993
      api_(api), only_verify_leaf_cert_crl_(config.only_verify_leaf_cert_crl()),
45
5993
      max_verify_depth_(config.has_max_verify_depth()
46
5993
                            ? absl::optional<uint32_t>(config.max_verify_depth().value())
47
5993
                            : absl::nullopt),
48
5993
      auto_sni_san_match_(auto_sni_san_match) {}
49

            
50
absl::StatusOr<std::unique_ptr<CertificateValidationContextConfigImpl>>
51
CertificateValidationContextConfigImpl::create(
52
    const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& context,
53
5996
    bool auto_sni_san_match, Api::Api& api, const std::string& name) {
54
5996
  bool allow_empty_trusted_ca = !context.has_trusted_ca();
55
5996
  if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.reject_empty_trusted_ca_file")) {
56
3
    allow_empty_trusted_ca = true;
57
3
  }
58
5996
  auto ca_or_error = Config::DataSource::read(context.trusted_ca(), allow_empty_trusted_ca, api);
59
5996
  RETURN_IF_NOT_OK_REF(ca_or_error.status());
60
5993
  auto list_or_error = Config::DataSource::read(context.crl(), true, api);
61
5993
  RETURN_IF_NOT_OK_REF(list_or_error.status());
62
5993
  auto config = std::unique_ptr<CertificateValidationContextConfigImpl>(
63
5993
      new CertificateValidationContextConfigImpl(*ca_or_error, *list_or_error, context,
64
5993
                                                 auto_sni_san_match, api, name));
65
5993
  absl::Status status = config->initialize();
66
5993
  if (status.ok()) {
67
5983
    return config;
68
5983
  }
69
10
  return status;
70
5993
}
71

            
72
5993
absl::Status CertificateValidationContextConfigImpl::initialize() {
73
5993
  if (ca_cert_.empty() && custom_validator_config_ == absl::nullopt) {
74
52
    if (!certificate_revocation_list_.empty()) {
75
3
      return absl::InvalidArgumentError(fmt::format("Failed to load CRL from {} without trusted CA",
76
3
                                                    certificateRevocationListPath()));
77
3
    }
78
49
    if (!subject_alt_name_matchers_.empty()) {
79
3
      return absl::InvalidArgumentError("SAN-based verification of peer certificates without "
80
3
                                        "trusted CA is insecure and not allowed");
81
3
    }
82
46
    if (allow_expired_certificate_) {
83
4
      return absl::InvalidArgumentError(
84
4
          "Certificate validity period is always ignored without trusted CA");
85
4
    }
86
46
  }
87
5983
  return absl::OkStatus();
88
5993
}
89

            
90
std::vector<envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher>
91
CertificateValidationContextConfigImpl::getSubjectAltNameMatchers(
92
5993
    const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config) {
93
5993
  std::vector<envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher>
94
5993
      subject_alt_name_matchers(config.match_typed_subject_alt_names().begin(),
95
5993
                                config.match_typed_subject_alt_names().end());
96
  // If typed subject alt name matchers are provided in the config, don't check
97
  // for the deprecated non-typed field.
98
5993
  if (!subject_alt_name_matchers.empty()) {
99
    // Warn that we're ignoring the deprecated san matcher field, if both are
100
    // specified.
101
1616
    if (!config.match_subject_alt_names().empty()) {
102
1
      ENVOY_LOG_MISC(warn,
103
1
                     "Ignoring match_subject_alt_names as match_typed_subject_alt_names is also "
104
1
                     "specified, and the former is deprecated.");
105
1
    }
106
1616
    return subject_alt_name_matchers;
107
1616
  }
108
  // Handle deprecated string type san matchers without san type specified, by
109
  // creating a matcher for each supported type.
110
  // Note: This does not handle otherName type
111
4377
  for (const envoy::type::matcher::v3::StringMatcher& matcher : config.match_subject_alt_names()) {
112
9
    static constexpr std::array<
113
9
        envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::SanType, 4>
114
9
        san_types{envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS,
115
9
                  envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::URI,
116
9
                  envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::EMAIL,
117
9
                  envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::IP_ADDRESS};
118
36
    for (const auto san_type : san_types) {
119
36
      subject_alt_name_matchers.emplace_back();
120
36
      subject_alt_name_matchers.back().set_san_type(san_type);
121
36
      *subject_alt_name_matchers.back().mutable_matcher() = matcher;
122
36
    }
123
9
  }
124
4377
  return subject_alt_name_matchers;
125
5993
}
126

            
127
} // namespace Ssl
128
} // namespace Envoy