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