1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/cpp/client/secure_credentials.h"
20
21 #include <string.h>
22
23 #include <algorithm>
24 #include <map>
25 #include <utility>
26
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/str_join.h"
30 #include "absl/types/optional.h"
31
32 #include <grpc/event_engine/event_engine.h>
33 #include <grpc/grpc_security_constants.h>
34 #include <grpc/slice.h>
35 #include <grpc/support/json.h>
36 #include <grpc/support/log.h>
37 #include <grpc/support/string_util.h>
38 #include <grpc/support/time.h>
39 #include <grpcpp/channel.h>
40 #include <grpcpp/impl/grpc_library.h>
41 #include <grpcpp/security/tls_credentials_options.h>
42 #include <grpcpp/support/channel_arguments.h>
43 #include <grpcpp/support/config.h>
44 #include <grpcpp/support/slice.h>
45 #include <grpcpp/support/status.h>
46
47 #include "src/core/lib/event_engine/default_event_engine.h"
48 #include "src/core/lib/gprpp/env.h"
49 #include "src/core/lib/gprpp/status_helper.h"
50 #include "src/core/lib/iomgr/error.h"
51 #include "src/core/lib/iomgr/load_file.h"
52 #include "src/core/lib/json/json.h"
53 #include "src/core/lib/json/json_reader.h"
54 #include "src/core/lib/security/util/json_util.h"
55 #include "src/cpp/client/create_channel_internal.h"
56 #include "src/cpp/common/secure_auth_context.h"
57
58 namespace grpc {
59
SecureChannelCredentials(grpc_channel_credentials * c_creds)60 SecureChannelCredentials::SecureChannelCredentials(
61 grpc_channel_credentials* c_creds)
62 : c_creds_(c_creds) {}
63
CreateChannelImpl(const std::string & target,const ChannelArguments & args)64 std::shared_ptr<Channel> SecureChannelCredentials::CreateChannelImpl(
65 const std::string& target, const ChannelArguments& args) {
66 return CreateChannelWithInterceptors(
67 target, args,
68 std::vector<std::unique_ptr<
69 grpc::experimental::ClientInterceptorFactoryInterface>>());
70 }
71
72 std::shared_ptr<Channel>
CreateChannelWithInterceptors(const std::string & target,const ChannelArguments & args,std::vector<std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>> interceptor_creators)73 SecureChannelCredentials::CreateChannelWithInterceptors(
74 const std::string& target, const ChannelArguments& args,
75 std::vector<
76 std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>
77 interceptor_creators) {
78 grpc_channel_args channel_args;
79 args.SetChannelArgs(&channel_args);
80 return grpc::CreateChannelInternal(
81 args.GetSslTargetNameOverride(),
82 grpc_channel_create(target.c_str(), c_creds_, &channel_args),
83 std::move(interceptor_creators));
84 }
85
SecureCallCredentials(grpc_call_credentials * c_creds)86 SecureCallCredentials::SecureCallCredentials(grpc_call_credentials* c_creds)
87 : c_creds_(c_creds) {}
88
ApplyToCall(grpc_call * call)89 bool SecureCallCredentials::ApplyToCall(grpc_call* call) {
90 return grpc_call_set_credentials(call, c_creds_) == GRPC_CALL_OK;
91 }
92
93 namespace internal {
94
WrapChannelCredentials(grpc_channel_credentials * creds)95 std::shared_ptr<ChannelCredentials> WrapChannelCredentials(
96 grpc_channel_credentials* creds) {
97 return creds == nullptr ? nullptr
98 : std::shared_ptr<ChannelCredentials>(
99 new SecureChannelCredentials(creds));
100 }
101
102 } // namespace internal
103
104 namespace {
105
WrapCallCredentials(grpc_call_credentials * creds)106 std::shared_ptr<CallCredentials> WrapCallCredentials(
107 grpc_call_credentials* creds) {
108 return creds == nullptr ? nullptr
109 : std::shared_ptr<CallCredentials>(
110 new SecureCallCredentials(creds));
111 }
112 } // namespace
113
GoogleDefaultCredentials()114 std::shared_ptr<ChannelCredentials> GoogleDefaultCredentials() {
115 grpc::internal::GrpcLibrary init; // To call grpc_init().
116 return internal::WrapChannelCredentials(
117 grpc_google_default_credentials_create(nullptr));
118 }
119
ExternalAccountCredentials(const grpc::string & json_string,const std::vector<grpc::string> & scopes)120 std::shared_ptr<CallCredentials> ExternalAccountCredentials(
121 const grpc::string& json_string, const std::vector<grpc::string>& scopes) {
122 grpc::internal::GrpcLibrary init; // To call grpc_init().
123 return WrapCallCredentials(grpc_external_account_credentials_create(
124 json_string.c_str(), absl::StrJoin(scopes, ",").c_str()));
125 }
126
127 // Builds SSL Credentials given SSL specific options
SslCredentials(const SslCredentialsOptions & options)128 std::shared_ptr<ChannelCredentials> SslCredentials(
129 const SslCredentialsOptions& options) {
130 grpc::internal::GrpcLibrary init; // To call grpc_init().
131 grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {
132 options.pem_private_key.c_str(), options.pem_cert_chain.c_str()};
133
134 grpc_channel_credentials* c_creds = grpc_ssl_credentials_create(
135 options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(),
136 options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr,
137 nullptr);
138 return internal::WrapChannelCredentials(c_creds);
139 }
140
141 namespace experimental {
142
143 namespace {
144
ClearStsCredentialsOptions(StsCredentialsOptions * options)145 void ClearStsCredentialsOptions(StsCredentialsOptions* options) {
146 if (options == nullptr) return;
147 options->token_exchange_service_uri.clear();
148 options->resource.clear();
149 options->audience.clear();
150 options->scope.clear();
151 options->requested_token_type.clear();
152 options->subject_token_path.clear();
153 options->subject_token_type.clear();
154 options->actor_token_path.clear();
155 options->actor_token_type.clear();
156 }
157
158 } // namespace
159
160 // Builds STS credentials options from JSON.
StsCredentialsOptionsFromJson(const std::string & json_string,StsCredentialsOptions * options)161 grpc::Status StsCredentialsOptionsFromJson(const std::string& json_string,
162 StsCredentialsOptions* options) {
163 if (options == nullptr) {
164 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
165 "options cannot be nullptr.");
166 }
167 ClearStsCredentialsOptions(options);
168 auto json = grpc_core::JsonParse(json_string.c_str());
169 if (!json.ok() || json->type() != grpc_core::Json::Type::kObject) {
170 return grpc::Status(
171 grpc::StatusCode::INVALID_ARGUMENT,
172 absl::StrCat("Invalid json: ", json.status().ToString()));
173 }
174
175 // Required fields.
176 const char* value = grpc_json_get_string_property(
177 *json, "token_exchange_service_uri", nullptr);
178 if (value == nullptr) {
179 ClearStsCredentialsOptions(options);
180 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
181 "token_exchange_service_uri must be specified.");
182 }
183 options->token_exchange_service_uri.assign(value);
184 value = grpc_json_get_string_property(*json, "subject_token_path", nullptr);
185 if (value == nullptr) {
186 ClearStsCredentialsOptions(options);
187 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
188 "subject_token_path must be specified.");
189 }
190 options->subject_token_path.assign(value);
191 value = grpc_json_get_string_property(*json, "subject_token_type", nullptr);
192 if (value == nullptr) {
193 ClearStsCredentialsOptions(options);
194 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
195 "subject_token_type must be specified.");
196 }
197 options->subject_token_type.assign(value);
198
199 // Optional fields.
200 value = grpc_json_get_string_property(*json, "resource", nullptr);
201 if (value != nullptr) options->resource.assign(value);
202 value = grpc_json_get_string_property(*json, "audience", nullptr);
203 if (value != nullptr) options->audience.assign(value);
204 value = grpc_json_get_string_property(*json, "scope", nullptr);
205 if (value != nullptr) options->scope.assign(value);
206 value = grpc_json_get_string_property(*json, "requested_token_type", nullptr);
207 if (value != nullptr) options->requested_token_type.assign(value);
208 value = grpc_json_get_string_property(*json, "actor_token_path", nullptr);
209 if (value != nullptr) options->actor_token_path.assign(value);
210 value = grpc_json_get_string_property(*json, "actor_token_type", nullptr);
211 if (value != nullptr) options->actor_token_type.assign(value);
212
213 return grpc::Status();
214 }
215
216 // Builds STS credentials Options from the $STS_CREDENTIALS env var.
StsCredentialsOptionsFromEnv(StsCredentialsOptions * options)217 grpc::Status StsCredentialsOptionsFromEnv(StsCredentialsOptions* options) {
218 if (options == nullptr) {
219 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
220 "options cannot be nullptr.");
221 }
222 ClearStsCredentialsOptions(options);
223 grpc_slice json_string = grpc_empty_slice();
224 auto sts_creds_path = grpc_core::GetEnv("STS_CREDENTIALS");
225 grpc_error_handle error;
226 grpc::Status status;
227 // NOLINTNEXTLINE(clang-diagnostic-unused-lambda-capture)
228 auto cleanup = [&json_string, &status]() {
229 grpc_slice_unref(json_string);
230 return status;
231 };
232 if (!sts_creds_path.has_value()) {
233 status = grpc::Status(grpc::StatusCode::NOT_FOUND,
234 "STS_CREDENTIALS environment variable not set.");
235 return cleanup();
236 }
237 error = grpc_load_file(sts_creds_path->c_str(), 1, &json_string);
238 if (!error.ok()) {
239 status = grpc::Status(grpc::StatusCode::NOT_FOUND,
240 grpc_core::StatusToString(error));
241 return cleanup();
242 }
243 status = StsCredentialsOptionsFromJson(
244 reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(json_string)),
245 options);
246 return cleanup();
247 }
248
249 // C++ to Core STS Credentials options.
StsCredentialsCppToCoreOptions(const StsCredentialsOptions & options)250 grpc_sts_credentials_options StsCredentialsCppToCoreOptions(
251 const StsCredentialsOptions& options) {
252 grpc_sts_credentials_options opts;
253 memset(&opts, 0, sizeof(opts));
254 opts.token_exchange_service_uri = options.token_exchange_service_uri.c_str();
255 opts.resource = options.resource.c_str();
256 opts.audience = options.audience.c_str();
257 opts.scope = options.scope.c_str();
258 opts.requested_token_type = options.requested_token_type.c_str();
259 opts.subject_token_path = options.subject_token_path.c_str();
260 opts.subject_token_type = options.subject_token_type.c_str();
261 opts.actor_token_path = options.actor_token_path.c_str();
262 opts.actor_token_type = options.actor_token_type.c_str();
263 return opts;
264 }
265
266 // Builds STS credentials.
StsCredentials(const StsCredentialsOptions & options)267 std::shared_ptr<CallCredentials> StsCredentials(
268 const StsCredentialsOptions& options) {
269 auto opts = StsCredentialsCppToCoreOptions(options);
270 return WrapCallCredentials(grpc_sts_credentials_create(&opts, nullptr));
271 }
272
MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin> plugin,grpc_security_level min_security_level)273 std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
274 std::unique_ptr<MetadataCredentialsPlugin> plugin,
275 grpc_security_level min_security_level) {
276 grpc::internal::GrpcLibrary init; // To call grpc_init().
277 const char* type = plugin->GetType();
278 grpc::MetadataCredentialsPluginWrapper* wrapper =
279 new grpc::MetadataCredentialsPluginWrapper(std::move(plugin));
280 grpc_metadata_credentials_plugin c_plugin = {
281 grpc::MetadataCredentialsPluginWrapper::GetMetadata,
282 grpc::MetadataCredentialsPluginWrapper::DebugString,
283 grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
284 return WrapCallCredentials(grpc_metadata_credentials_create_from_plugin(
285 c_plugin, min_security_level, nullptr));
286 }
287
288 // Builds ALTS Credentials given ALTS specific options
AltsCredentials(const AltsCredentialsOptions & options)289 std::shared_ptr<ChannelCredentials> AltsCredentials(
290 const AltsCredentialsOptions& options) {
291 grpc::internal::GrpcLibrary init; // To call grpc_init().
292 grpc_alts_credentials_options* c_options =
293 grpc_alts_credentials_client_options_create();
294 for (const auto& service_account : options.target_service_accounts) {
295 grpc_alts_credentials_client_options_add_target_service_account(
296 c_options, service_account.c_str());
297 }
298 grpc_channel_credentials* c_creds = grpc_alts_credentials_create(c_options);
299 grpc_alts_credentials_options_destroy(c_options);
300 return internal::WrapChannelCredentials(c_creds);
301 }
302
303 // Builds Local Credentials
LocalCredentials(grpc_local_connect_type type)304 std::shared_ptr<ChannelCredentials> LocalCredentials(
305 grpc_local_connect_type type) {
306 grpc::internal::GrpcLibrary init; // To call grpc_init().
307 return internal::WrapChannelCredentials(grpc_local_credentials_create(type));
308 }
309
310 // Builds TLS Credentials given TLS options.
TlsCredentials(const TlsChannelCredentialsOptions & options)311 std::shared_ptr<ChannelCredentials> TlsCredentials(
312 const TlsChannelCredentialsOptions& options) {
313 return internal::WrapChannelCredentials(
314 grpc_tls_credentials_create(options.c_credentials_options()));
315 }
316
317 } // namespace experimental
318
319 // Builds credentials for use when running in GCE
GoogleComputeEngineCredentials()320 std::shared_ptr<CallCredentials> GoogleComputeEngineCredentials() {
321 grpc::internal::GrpcLibrary init; // To call grpc_init().
322 return WrapCallCredentials(
323 grpc_google_compute_engine_credentials_create(nullptr));
324 }
325
326 // Builds JWT credentials.
ServiceAccountJWTAccessCredentials(const std::string & json_key,long token_lifetime_seconds)327 std::shared_ptr<CallCredentials> ServiceAccountJWTAccessCredentials(
328 const std::string& json_key, long token_lifetime_seconds) {
329 grpc::internal::GrpcLibrary init; // To call grpc_init().
330 if (token_lifetime_seconds <= 0) {
331 gpr_log(GPR_ERROR,
332 "Trying to create JWTCredentials with non-positive lifetime");
333 return WrapCallCredentials(nullptr);
334 }
335 gpr_timespec lifetime =
336 gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN);
337 return WrapCallCredentials(grpc_service_account_jwt_access_credentials_create(
338 json_key.c_str(), lifetime, nullptr));
339 }
340
341 // Builds refresh token credentials.
GoogleRefreshTokenCredentials(const std::string & json_refresh_token)342 std::shared_ptr<CallCredentials> GoogleRefreshTokenCredentials(
343 const std::string& json_refresh_token) {
344 grpc::internal::GrpcLibrary init; // To call grpc_init().
345 return WrapCallCredentials(grpc_google_refresh_token_credentials_create(
346 json_refresh_token.c_str(), nullptr));
347 }
348
349 // Builds access token credentials.
AccessTokenCredentials(const std::string & access_token)350 std::shared_ptr<CallCredentials> AccessTokenCredentials(
351 const std::string& access_token) {
352 grpc::internal::GrpcLibrary init; // To call grpc_init().
353 return WrapCallCredentials(
354 grpc_access_token_credentials_create(access_token.c_str(), nullptr));
355 }
356
357 // Builds IAM credentials.
GoogleIAMCredentials(const std::string & authorization_token,const std::string & authority_selector)358 std::shared_ptr<CallCredentials> GoogleIAMCredentials(
359 const std::string& authorization_token,
360 const std::string& authority_selector) {
361 grpc::internal::GrpcLibrary init; // To call grpc_init().
362 return WrapCallCredentials(grpc_google_iam_credentials_create(
363 authorization_token.c_str(), authority_selector.c_str(), nullptr));
364 }
365
366 // Combines one channel credentials and one call credentials into a channel
367 // composite credentials.
CompositeChannelCredentials(const std::shared_ptr<ChannelCredentials> & channel_creds,const std::shared_ptr<CallCredentials> & call_creds)368 std::shared_ptr<ChannelCredentials> CompositeChannelCredentials(
369 const std::shared_ptr<ChannelCredentials>& channel_creds,
370 const std::shared_ptr<CallCredentials>& call_creds) {
371 // Note that we are not saving shared_ptrs to the two credentials passed in
372 // here. This is OK because the underlying C objects (i.e., channel_creds and
373 // call_creds) into grpc_composite_credentials_create will see their refcounts
374 // incremented.
375 SecureChannelCredentials* s_channel_creds =
376 channel_creds->AsSecureCredentials();
377 SecureCallCredentials* s_call_creds = call_creds->AsSecureCredentials();
378 if (s_channel_creds && s_call_creds) {
379 return internal::WrapChannelCredentials(
380 grpc_composite_channel_credentials_create(
381 s_channel_creds->GetRawCreds(), s_call_creds->GetRawCreds(),
382 nullptr));
383 }
384 return nullptr;
385 }
386
CompositeCallCredentials(const std::shared_ptr<CallCredentials> & creds1,const std::shared_ptr<CallCredentials> & creds2)387 std::shared_ptr<CallCredentials> CompositeCallCredentials(
388 const std::shared_ptr<CallCredentials>& creds1,
389 const std::shared_ptr<CallCredentials>& creds2) {
390 SecureCallCredentials* s_creds1 = creds1->AsSecureCredentials();
391 SecureCallCredentials* s_creds2 = creds2->AsSecureCredentials();
392 if (s_creds1 != nullptr && s_creds2 != nullptr) {
393 return WrapCallCredentials(grpc_composite_call_credentials_create(
394 s_creds1->GetRawCreds(), s_creds2->GetRawCreds(), nullptr));
395 }
396 return nullptr;
397 }
398
MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin> plugin)399 std::shared_ptr<CallCredentials> MetadataCredentialsFromPlugin(
400 std::unique_ptr<MetadataCredentialsPlugin> plugin) {
401 grpc::internal::GrpcLibrary init; // To call grpc_init().
402 const char* type = plugin->GetType();
403 grpc::MetadataCredentialsPluginWrapper* wrapper =
404 new grpc::MetadataCredentialsPluginWrapper(std::move(plugin));
405 grpc_metadata_credentials_plugin c_plugin = {
406 grpc::MetadataCredentialsPluginWrapper::GetMetadata,
407 grpc::MetadataCredentialsPluginWrapper::DebugString,
408 grpc::MetadataCredentialsPluginWrapper::Destroy, wrapper, type};
409 return WrapCallCredentials(grpc_metadata_credentials_create_from_plugin(
410 c_plugin, GRPC_PRIVACY_AND_INTEGRITY, nullptr));
411 }
412
DebugString(void * wrapper)413 char* MetadataCredentialsPluginWrapper::DebugString(void* wrapper) {
414 GPR_ASSERT(wrapper);
415 MetadataCredentialsPluginWrapper* w =
416 static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
417 return gpr_strdup(w->plugin_->DebugString().c_str());
418 }
419
Destroy(void * wrapper)420 void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) {
421 if (wrapper == nullptr) return;
422 grpc_event_engine::experimental::GetDefaultEventEngine()->Run([wrapper] {
423 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
424 grpc_core::ExecCtx exec_ctx;
425 delete static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
426 });
427 }
428
GetMetadata(void * wrapper,grpc_auth_metadata_context context,grpc_credentials_plugin_metadata_cb cb,void * user_data,grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],size_t * num_creds_md,grpc_status_code * status,const char ** error_details)429 int MetadataCredentialsPluginWrapper::GetMetadata(
430 void* wrapper, grpc_auth_metadata_context context,
431 grpc_credentials_plugin_metadata_cb cb, void* user_data,
432 grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
433 size_t* num_creds_md, grpc_status_code* status,
434 const char** error_details) {
435 GPR_ASSERT(wrapper);
436 MetadataCredentialsPluginWrapper* w =
437 static_cast<MetadataCredentialsPluginWrapper*>(wrapper);
438 if (!w->plugin_) {
439 *num_creds_md = 0;
440 *status = GRPC_STATUS_OK;
441 *error_details = nullptr;
442 return 1;
443 }
444 if (w->plugin_->IsBlocking()) {
445 // The internals of context may be destroyed if GetMetadata is cancelled.
446 // Make a copy for InvokePlugin.
447 grpc_auth_metadata_context context_copy = grpc_auth_metadata_context();
448 grpc_auth_metadata_context_copy(&context, &context_copy);
449 // Asynchronous return.
450 w->thread_pool_->Add([w, context_copy, cb, user_data]() mutable {
451 w->MetadataCredentialsPluginWrapper::InvokePlugin(
452 context_copy, cb, user_data, nullptr, nullptr, nullptr, nullptr);
453 grpc_auth_metadata_context_reset(&context_copy);
454 });
455 return 0;
456 } else {
457 // Synchronous return.
458 w->InvokePlugin(context, cb, user_data, creds_md, num_creds_md, status,
459 error_details);
460 return 1;
461 }
462 }
463
464 namespace {
465
UnrefMetadata(const std::vector<grpc_metadata> & md)466 void UnrefMetadata(const std::vector<grpc_metadata>& md) {
467 for (const auto& metadatum : md) {
468 grpc_slice_unref(metadatum.key);
469 grpc_slice_unref(metadatum.value);
470 }
471 }
472
473 } // namespace
474
InvokePlugin(grpc_auth_metadata_context context,grpc_credentials_plugin_metadata_cb cb,void * user_data,grpc_metadata creds_md[4],size_t * num_creds_md,grpc_status_code * status_code,const char ** error_details)475 void MetadataCredentialsPluginWrapper::InvokePlugin(
476 grpc_auth_metadata_context context, grpc_credentials_plugin_metadata_cb cb,
477 void* user_data, grpc_metadata creds_md[4], size_t* num_creds_md,
478 grpc_status_code* status_code, const char** error_details) {
479 std::multimap<std::string, std::string> metadata;
480
481 // const_cast is safe since the SecureAuthContext only inc/dec the refcount
482 // and the object is passed as a const ref to plugin_->GetMetadata.
483 SecureAuthContext cpp_channel_auth_context(
484 const_cast<grpc_auth_context*>(context.channel_auth_context));
485
486 Status status = plugin_->GetMetadata(context.service_url, context.method_name,
487 cpp_channel_auth_context, &metadata);
488 std::vector<grpc_metadata> md;
489 for (auto& metadatum : metadata) {
490 grpc_metadata md_entry;
491 md_entry.key = SliceFromCopiedString(metadatum.first);
492 md_entry.value = SliceFromCopiedString(metadatum.second);
493 md.push_back(md_entry);
494 }
495 if (creds_md != nullptr) {
496 // Synchronous return.
497 if (md.size() > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
498 *num_creds_md = 0;
499 *status_code = GRPC_STATUS_INTERNAL;
500 *error_details = gpr_strdup(
501 "blocking plugin credentials returned too many metadata keys");
502 UnrefMetadata(md);
503 } else {
504 for (const auto& elem : md) {
505 creds_md[*num_creds_md].key = elem.key;
506 creds_md[*num_creds_md].value = elem.value;
507 ++(*num_creds_md);
508 }
509 *status_code = static_cast<grpc_status_code>(status.error_code());
510 *error_details =
511 status.ok() ? nullptr : gpr_strdup(status.error_message().c_str());
512 }
513 } else {
514 // Asynchronous return.
515 cb(user_data, md.empty() ? nullptr : &md[0], md.size(),
516 static_cast<grpc_status_code>(status.error_code()),
517 status.error_message().c_str());
518 UnrefMetadata(md);
519 }
520 }
521
MetadataCredentialsPluginWrapper(std::unique_ptr<MetadataCredentialsPlugin> plugin)522 MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper(
523 std::unique_ptr<MetadataCredentialsPlugin> plugin)
524 : plugin_(std::move(plugin)) {
525 if (plugin_->IsBlocking()) {
526 thread_pool_.reset(CreateDefaultThreadPool());
527 }
528 }
529
530 } // namespace grpc
531