• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "src/core/xds/grpc/xds_common_types_parser.h"
18 
19 #include <grpc/support/json.h>
20 #include <grpc/support/port_platform.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 
24 #include <algorithm>
25 #include <map>
26 #include <utility>
27 
28 #include "absl/status/status.h"
29 #include "absl/status/statusor.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_format.h"
32 #include "absl/strings/str_join.h"
33 #include "envoy/config/core/v3/address.upb.h"
34 #include "envoy/extensions/transport_sockets/tls/v3/common.upb.h"
35 #include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h"
36 #include "envoy/type/matcher/v3/regex.upb.h"
37 #include "envoy/type/matcher/v3/string.upb.h"
38 #include "google/protobuf/any.upb.h"
39 #include "google/protobuf/struct.upb.h"
40 #include "google/protobuf/struct.upbdefs.h"
41 #include "google/protobuf/wrappers.upb.h"
42 #include "src/core/lib/address_utils/parse_address.h"
43 #include "src/core/util/env.h"
44 #include "src/core/util/json/json_reader.h"
45 #include "src/core/util/upb_utils.h"
46 #include "src/core/xds/grpc/xds_bootstrap_grpc.h"
47 #include "src/core/xds/xds_client/xds_client.h"
48 #include "upb/base/status.hpp"
49 #include "upb/json/encode.h"
50 #include "upb/mem/arena.h"
51 #include "xds/type/v3/typed_struct.upb.h"
52 
53 namespace grpc_core {
54 
55 //
56 // ParseDuration()
57 //
58 
ParseDuration(const google_protobuf_Duration * proto_duration,ValidationErrors * errors)59 Duration ParseDuration(const google_protobuf_Duration* proto_duration,
60                        ValidationErrors* errors) {
61   int64_t seconds = google_protobuf_Duration_seconds(proto_duration);
62   if (seconds < 0 || seconds > 315576000000) {
63     ValidationErrors::ScopedField field(errors, ".seconds");
64     errors->AddError("value must be in the range [0, 315576000000]");
65   }
66   int32_t nanos = google_protobuf_Duration_nanos(proto_duration);
67   if (nanos < 0 || nanos > 999999999) {
68     ValidationErrors::ScopedField field(errors, ".nanos");
69     errors->AddError("value must be in the range [0, 999999999]");
70   }
71   return Duration::FromSecondsAndNanoseconds(seconds, nanos);
72 }
73 
74 //
75 // ParseXdsAddress()
76 //
77 
ParseXdsAddress(const envoy_config_core_v3_Address * address,ValidationErrors * errors)78 absl::optional<grpc_resolved_address> ParseXdsAddress(
79     const envoy_config_core_v3_Address* address, ValidationErrors* errors) {
80   if (address == nullptr) {
81     errors->AddError("field not present");
82     return absl::nullopt;
83   }
84   ValidationErrors::ScopedField field(errors, ".socket_address");
85   const envoy_config_core_v3_SocketAddress* socket_address =
86       envoy_config_core_v3_Address_socket_address(address);
87   if (socket_address == nullptr) {
88     errors->AddError("field not present");
89     return absl::nullopt;
90   }
91   std::string address_str = UpbStringToStdString(
92       envoy_config_core_v3_SocketAddress_address(socket_address));
93   uint32_t port;
94   {
95     ValidationErrors::ScopedField field(errors, ".port_value");
96     port = envoy_config_core_v3_SocketAddress_port_value(socket_address);
97     if (GPR_UNLIKELY(port >> 16) != 0) {
98       errors->AddError("invalid port");
99       return absl::nullopt;
100     }
101   }
102   auto addr = StringToSockaddr(address_str, port);
103   if (!addr.ok()) {
104     errors->AddError(addr.status().message());
105     return absl::nullopt;
106   }
107   return *addr;
108 }
109 
110 //
111 // CommonTlsContextParse()
112 //
113 
114 namespace {
115 
XdsSystemRootCertsEnabled()116 bool XdsSystemRootCertsEnabled() {
117   auto value = GetEnv("GRPC_EXPERIMENTAL_XDS_SYSTEM_ROOT_CERTS");
118   if (!value.has_value()) return false;
119   bool parsed_value;
120   bool parse_succeeded = gpr_parse_bool_value(value->c_str(), &parsed_value);
121   return parse_succeeded && parsed_value;
122 }
123 
124 // CertificateProviderInstance is deprecated but we are still supporting it for
125 // backward compatibility reasons. Note that we still parse the data into the
126 // same CertificateProviderPluginInstance struct since the fields are the same.
127 // TODO(yashykt): Remove this once we stop supporting the old way of fetching
128 // certificate provider instances.
129 CommonTlsContext::CertificateProviderPluginInstance
CertificateProviderInstanceParse(const XdsResourceType::DecodeContext & context,const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance * certificate_provider_instance_proto,ValidationErrors * errors)130 CertificateProviderInstanceParse(
131     const XdsResourceType::DecodeContext& context,
132     const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance*
133         certificate_provider_instance_proto,
134     ValidationErrors* errors) {
135   CommonTlsContext::CertificateProviderPluginInstance cert_provider;
136   cert_provider.instance_name = UpbStringToStdString(
137       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
138           certificate_provider_instance_proto));
139   const auto& bootstrap =
140       static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap());
141   if (bootstrap.certificate_providers().find(cert_provider.instance_name) ==
142       bootstrap.certificate_providers().end()) {
143     ValidationErrors::ScopedField field(errors, ".instance_name");
144     errors->AddError(
145         absl::StrCat("unrecognized certificate provider instance name: ",
146                      cert_provider.instance_name));
147   }
148   cert_provider.certificate_name = UpbStringToStdString(
149       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
150           certificate_provider_instance_proto));
151   return cert_provider;
152 }
153 
154 CommonTlsContext::CertificateProviderPluginInstance
CertificateProviderPluginInstanceParse(const XdsResourceType::DecodeContext & context,const envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance * certificate_provider_plugin_instance_proto,ValidationErrors * errors)155 CertificateProviderPluginInstanceParse(
156     const XdsResourceType::DecodeContext& context,
157     const envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance*
158         certificate_provider_plugin_instance_proto,
159     ValidationErrors* errors) {
160   CommonTlsContext::CertificateProviderPluginInstance cert_provider;
161   cert_provider.instance_name = UpbStringToStdString(
162       envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_instance_name(
163           certificate_provider_plugin_instance_proto));
164   const auto& bootstrap =
165       static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap());
166   if (bootstrap.certificate_providers().find(cert_provider.instance_name) ==
167       bootstrap.certificate_providers().end()) {
168     ValidationErrors::ScopedField field(errors, ".instance_name");
169     errors->AddError(
170         absl::StrCat("unrecognized certificate provider instance name: ",
171                      cert_provider.instance_name));
172   }
173   cert_provider.certificate_name = UpbStringToStdString(
174       envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_certificate_name(
175           certificate_provider_plugin_instance_proto));
176   return cert_provider;
177 }
178 
179 CommonTlsContext::CertificateValidationContext
CertificateValidationContextParse(const XdsResourceType::DecodeContext & context,const envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext * certificate_validation_context_proto,ValidationErrors * errors)180 CertificateValidationContextParse(
181     const XdsResourceType::DecodeContext& context,
182     const envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext*
183         certificate_validation_context_proto,
184     ValidationErrors* errors) {
185   CommonTlsContext::CertificateValidationContext certificate_validation_context;
186   size_t len = 0;
187   auto* subject_alt_names_matchers =
188       envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
189           certificate_validation_context_proto, &len);
190   for (size_t i = 0; i < len; ++i) {
191     ValidationErrors::ScopedField field(
192         errors, absl::StrCat(".match_subject_alt_names[", i, "]"));
193     StringMatcher::Type type;
194     std::string matcher;
195     if (envoy_type_matcher_v3_StringMatcher_has_exact(
196             subject_alt_names_matchers[i])) {
197       type = StringMatcher::Type::kExact;
198       matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact(
199           subject_alt_names_matchers[i]));
200     } else if (envoy_type_matcher_v3_StringMatcher_has_prefix(
201                    subject_alt_names_matchers[i])) {
202       type = StringMatcher::Type::kPrefix;
203       matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix(
204           subject_alt_names_matchers[i]));
205     } else if (envoy_type_matcher_v3_StringMatcher_has_suffix(
206                    subject_alt_names_matchers[i])) {
207       type = StringMatcher::Type::kSuffix;
208       matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix(
209           subject_alt_names_matchers[i]));
210     } else if (envoy_type_matcher_v3_StringMatcher_has_contains(
211                    subject_alt_names_matchers[i])) {
212       type = StringMatcher::Type::kContains;
213       matcher =
214           UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains(
215               subject_alt_names_matchers[i]));
216     } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(
217                    subject_alt_names_matchers[i])) {
218       type = StringMatcher::Type::kSafeRegex;
219       auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex(
220           subject_alt_names_matchers[i]);
221       matcher = UpbStringToStdString(
222           envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
223     } else {
224       errors->AddError("invalid StringMatcher specified");
225       continue;
226     }
227     bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
228         subject_alt_names_matchers[i]);
229     absl::StatusOr<StringMatcher> string_matcher =
230         StringMatcher::Create(type, matcher,
231                               /*case_sensitive=*/!ignore_case);
232     if (!string_matcher.ok()) {
233       errors->AddError(string_matcher.status().message());
234       continue;
235     }
236     if (type == StringMatcher::Type::kSafeRegex && ignore_case) {
237       ValidationErrors::ScopedField field(errors, ".ignore_case");
238       errors->AddError("not supported for regex matcher");
239       continue;
240     }
241     certificate_validation_context.match_subject_alt_names.push_back(
242         std::move(string_matcher.value()));
243   }
244   auto* ca_certificate_provider_instance =
245       envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_ca_certificate_provider_instance(
246           certificate_validation_context_proto);
247   if (ca_certificate_provider_instance != nullptr) {
248     ValidationErrors::ScopedField field(errors,
249                                         ".ca_certificate_provider_instance");
250     certificate_validation_context.ca_certs =
251         CertificateProviderPluginInstanceParse(
252             context, ca_certificate_provider_instance, errors);
253   } else if (XdsSystemRootCertsEnabled()) {
254     auto* system_root_certs =
255         envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_system_root_certs(
256             certificate_validation_context_proto);
257     if (system_root_certs != nullptr) {
258       certificate_validation_context.ca_certs =
259           CommonTlsContext::CertificateValidationContext::SystemRootCerts();
260     }
261   }
262   if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_spki(
263           certificate_validation_context_proto, nullptr) != nullptr) {
264     ValidationErrors::ScopedField field(errors, ".verify_certificate_spki");
265     errors->AddError("feature unsupported");
266   }
267   if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_hash(
268           certificate_validation_context_proto, nullptr) != nullptr) {
269     ValidationErrors::ScopedField field(errors, ".verify_certificate_hash");
270     errors->AddError("feature unsupported");
271   }
272   if (ParseBoolValue(
273           envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_require_signed_certificate_timestamp(
274               certificate_validation_context_proto))) {
275     ValidationErrors::ScopedField field(
276         errors, ".require_signed_certificate_timestamp");
277     errors->AddError("feature unsupported");
278   }
279   if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_crl(
280           certificate_validation_context_proto)) {
281     ValidationErrors::ScopedField field(errors, ".crl");
282     errors->AddError("feature unsupported");
283   }
284   if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_custom_validator_config(
285           certificate_validation_context_proto)) {
286     ValidationErrors::ScopedField field(errors, ".custom_validator_config");
287     errors->AddError("feature unsupported");
288   }
289   return certificate_validation_context;
290 }
291 
292 }  // namespace
293 
CommonTlsContextParse(const XdsResourceType::DecodeContext & context,const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext * common_tls_context_proto,ValidationErrors * errors)294 CommonTlsContext CommonTlsContextParse(
295     const XdsResourceType::DecodeContext& context,
296     const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
297         common_tls_context_proto,
298     ValidationErrors* errors) {
299   CommonTlsContext common_tls_context;
300   // The validation context is derived from the oneof in
301   // 'validation_context_type'. 'validation_context_sds_secret_config' is not
302   // supported.
303   auto* combined_validation_context =
304       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context(
305           common_tls_context_proto);
306   if (combined_validation_context != nullptr) {
307     ValidationErrors::ScopedField field(errors, ".combined_validation_context");
308     auto* default_validation_context =
309         envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context(
310             combined_validation_context);
311     if (default_validation_context != nullptr) {
312       ValidationErrors::ScopedField field(errors,
313                                           ".default_validation_context");
314       common_tls_context.certificate_validation_context =
315           CertificateValidationContextParse(context, default_validation_context,
316                                             errors);
317     }
318     // If after parsing default_validation_context,
319     // common_tls_context->certificate_validation_context.ca_certs does not
320     // contain a cert provider, fall back onto
321     // 'validation_context_certificate_provider_instance' inside
322     // 'combined_validation_context'. Note that this way of fetching root
323     // certificates is deprecated and will be removed in the future.
324     // TODO(yashykt): Remove this once it's no longer needed.
325     if (!absl::holds_alternative<
326             CommonTlsContext::CertificateProviderPluginInstance>(
327             common_tls_context.certificate_validation_context.ca_certs)) {
328       const auto* validation_context_certificate_provider_instance =
329           envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance(
330               combined_validation_context);
331       if (validation_context_certificate_provider_instance != nullptr) {
332         ValidationErrors::ScopedField field(
333             errors, ".validation_context_certificate_provider_instance");
334         common_tls_context.certificate_validation_context.ca_certs =
335             CertificateProviderInstanceParse(
336                 context, validation_context_certificate_provider_instance,
337                 errors);
338       }
339     }
340   } else {
341     auto* validation_context =
342         envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_validation_context(
343             common_tls_context_proto);
344     if (validation_context != nullptr) {
345       ValidationErrors::ScopedField field(errors, ".validation_context");
346       common_tls_context.certificate_validation_context =
347           CertificateValidationContextParse(context, validation_context,
348                                             errors);
349     } else if (
350         envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_validation_context_sds_secret_config(
351             common_tls_context_proto)) {
352       ValidationErrors::ScopedField field(
353           errors, ".validation_context_sds_secret_config");
354       errors->AddError("feature unsupported");
355     }
356   }
357   auto* tls_certificate_provider_instance =
358       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_provider_instance(
359           common_tls_context_proto);
360   if (tls_certificate_provider_instance != nullptr) {
361     ValidationErrors::ScopedField field(errors,
362                                         ".tls_certificate_provider_instance");
363     common_tls_context.tls_certificate_provider_instance =
364         CertificateProviderPluginInstanceParse(
365             context, tls_certificate_provider_instance, errors);
366   } else {
367     // Fall back onto 'tls_certificate_certificate_provider_instance'. Note that
368     // this way of fetching identity certificates is deprecated and will be
369     // removed in the future.
370     // TODO(yashykt): Remove this once it's no longer needed.
371     auto* tls_certificate_certificate_provider_instance =
372         envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance(
373             common_tls_context_proto);
374     if (tls_certificate_certificate_provider_instance != nullptr) {
375       ValidationErrors::ScopedField field(
376           errors, ".tls_certificate_certificate_provider_instance");
377       common_tls_context.tls_certificate_provider_instance =
378           CertificateProviderInstanceParse(
379               context, tls_certificate_certificate_provider_instance, errors);
380     } else {
381       size_t size;
382       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificates(
383           common_tls_context_proto, &size);
384       if (size != 0) {
385         ValidationErrors::ScopedField field(errors, ".tls_certificates");
386         errors->AddError("feature unsupported");
387       }
388       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_sds_secret_configs(
389           common_tls_context_proto, &size);
390       if (size != 0) {
391         ValidationErrors::ScopedField field(
392             errors, ".tls_certificate_sds_secret_configs");
393         errors->AddError("feature unsupported");
394       }
395     }
396   }
397   if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_params(
398           common_tls_context_proto)) {
399     ValidationErrors::ScopedField field(errors, ".tls_params");
400     errors->AddError("feature unsupported");
401   }
402   if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_custom_handshaker(
403           common_tls_context_proto)) {
404     ValidationErrors::ScopedField field(errors, ".custom_handshaker");
405     errors->AddError("feature unsupported");
406   }
407   return common_tls_context;
408 }
409 
410 //
411 // ParseProtobufStructToJson()
412 //
413 
ParseProtobufStructToJson(const XdsResourceType::DecodeContext & context,const google_protobuf_Struct * resource)414 absl::StatusOr<Json> ParseProtobufStructToJson(
415     const XdsResourceType::DecodeContext& context,
416     const google_protobuf_Struct* resource) {
417   upb::Status status;
418   const auto* msg_def = google_protobuf_Struct_getmsgdef(context.symtab);
419   size_t json_size =
420       upb_JsonEncode(reinterpret_cast<const upb_Message*>(resource), msg_def,
421                      context.symtab, 0, nullptr, 0, status.ptr());
422   if (json_size == static_cast<size_t>(-1)) {
423     return absl::InvalidArgumentError(
424         absl::StrCat("error encoding google::Protobuf::Struct as JSON: ",
425                      upb_Status_ErrorMessage(status.ptr())));
426   }
427   void* buf = upb_Arena_Malloc(context.arena, json_size + 1);
428   upb_JsonEncode(reinterpret_cast<const upb_Message*>(resource), msg_def,
429                  context.symtab, 0, reinterpret_cast<char*>(buf), json_size + 1,
430                  status.ptr());
431   auto json = JsonParse(reinterpret_cast<char*>(buf));
432   if (!json.ok()) {
433     // This should never happen.
434     return absl::InternalError(
435         absl::StrCat("error parsing JSON form of google::Protobuf::Struct "
436                      "produced by upb library: ",
437                      json.status().ToString()));
438   }
439   return std::move(*json);
440 }
441 
442 //
443 // ExtractXdsExtension()
444 //
445 
ExtractXdsExtension(const XdsResourceType::DecodeContext & context,const google_protobuf_Any * any,ValidationErrors * errors)446 absl::optional<XdsExtension> ExtractXdsExtension(
447     const XdsResourceType::DecodeContext& context,
448     const google_protobuf_Any* any, ValidationErrors* errors) {
449   if (any == nullptr) {
450     errors->AddError("field not present");
451     return absl::nullopt;
452   }
453   XdsExtension extension;
454   auto strip_type_prefix = [&]() {
455     ValidationErrors::ScopedField field(errors, ".type_url");
456     if (extension.type.empty()) {
457       errors->AddError("field not present");
458       return false;
459     }
460     size_t pos = extension.type.rfind('/');
461     if (pos == absl::string_view::npos || pos == extension.type.size() - 1) {
462       errors->AddError(absl::StrCat("invalid value \"", extension.type, "\""));
463     } else {
464       extension.type = extension.type.substr(pos + 1);
465     }
466     return true;
467   };
468   extension.type = UpbStringToAbsl(google_protobuf_Any_type_url(any));
469   if (!strip_type_prefix()) return absl::nullopt;
470   extension.validation_fields.emplace_back(
471       errors, absl::StrCat(".value[", extension.type, "]"));
472   absl::string_view any_value = UpbStringToAbsl(google_protobuf_Any_value(any));
473   if (extension.type == "xds.type.v3.TypedStruct" ||
474       extension.type == "udpa.type.v1.TypedStruct") {
475     const auto* typed_struct = xds_type_v3_TypedStruct_parse(
476         any_value.data(), any_value.size(), context.arena);
477     if (typed_struct == nullptr) {
478       errors->AddError("could not parse");
479       return absl::nullopt;
480     }
481     extension.type =
482         UpbStringToAbsl(xds_type_v3_TypedStruct_type_url(typed_struct));
483     if (!strip_type_prefix()) return absl::nullopt;
484     extension.validation_fields.emplace_back(
485         errors, absl::StrCat(".value[", extension.type, "]"));
486     auto* protobuf_struct = xds_type_v3_TypedStruct_value(typed_struct);
487     if (protobuf_struct == nullptr) {
488       extension.value = Json::FromObject({});  // Default to empty object.
489     } else {
490       auto json = ParseProtobufStructToJson(context, protobuf_struct);
491       if (!json.ok()) {
492         errors->AddError(json.status().message());
493         return absl::nullopt;
494       }
495       extension.value = std::move(*json);
496     }
497   } else {
498     extension.value = any_value;
499   }
500   return std::move(extension);
501 }
502 
503 }  // namespace grpc_core
504