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 <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <algorithm>
27 #include <atomic>
28 #include <map>
29 #include <memory>
30 #include <vector>
31 
32 #include "absl/status/status.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/str_format.h"
35 #include "absl/strings/str_join.h"
36 #include "absl/strings/string_view.h"
37 
38 #include <grpc/grpc.h>
39 #include <grpc/grpc_security.h>
40 #include <grpc/slice.h>
41 #include <grpc/support/alloc.h>
42 #include <grpc/support/json.h>
43 #include <grpc/support/log.h>
44 #include <grpc/support/string_util.h>
45 #include <grpc/support/time.h>
46 
47 #include "src/core/lib/debug/trace.h"
48 #include "src/core/lib/gprpp/memory.h"
49 #include "src/core/lib/gprpp/ref_counted_ptr.h"
50 #include "src/core/lib/gprpp/status_helper.h"
51 #include "src/core/lib/http/httpcli_ssl_credentials.h"
52 #include "src/core/lib/iomgr/error.h"
53 #include "src/core/lib/iomgr/load_file.h"
54 #include "src/core/lib/iomgr/pollset_set.h"
55 #include "src/core/lib/json/json.h"
56 #include "src/core/lib/json/json_reader.h"
57 #include "src/core/lib/promise/context.h"
58 #include "src/core/lib/promise/poll.h"
59 #include "src/core/lib/promise/promise.h"
60 #include "src/core/lib/security/util/json_util.h"
61 #include "src/core/lib/surface/api_trace.h"
62 #include "src/core/lib/transport/error_utils.h"
63 #include "src/core/lib/transport/metadata_batch.h"
64 #include "src/core/lib/uri/uri_parser.h"
65 
66 using grpc_core::Json;
67 
68 //
69 // Auth Refresh Token.
70 //
71 
grpc_auth_refresh_token_is_valid(const grpc_auth_refresh_token * refresh_token)72 int grpc_auth_refresh_token_is_valid(
73     const grpc_auth_refresh_token* refresh_token) {
74   return (refresh_token != nullptr) &&
75          strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID) != 0;
76 }
77 
grpc_auth_refresh_token_create_from_json(const Json & json)78 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
79     const Json& json) {
80   grpc_auth_refresh_token result;
81   const char* prop_value;
82   int success = 0;
83   grpc_error_handle error;
84 
85   memset(&result, 0, sizeof(grpc_auth_refresh_token));
86   result.type = GRPC_AUTH_JSON_TYPE_INVALID;
87   if (json.type() != Json::Type::kObject) {
88     gpr_log(GPR_ERROR, "Invalid json.");
89     goto end;
90   }
91 
92   prop_value = grpc_json_get_string_property(json, "type", &error);
93   GRPC_LOG_IF_ERROR("Parsing refresh token", error);
94   if (prop_value == nullptr ||
95       strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER) != 0) {
96     goto end;
97   }
98   result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
99 
100   if (!grpc_copy_json_string_property(json, "client_secret",
101                                       &result.client_secret) ||
102       !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
103       !grpc_copy_json_string_property(json, "refresh_token",
104                                       &result.refresh_token)) {
105     goto end;
106   }
107   success = 1;
108 
109 end:
110   if (!success) grpc_auth_refresh_token_destruct(&result);
111   return result;
112 }
113 
grpc_auth_refresh_token_create_from_string(const char * json_string)114 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
115     const char* json_string) {
116   Json json;
117   auto json_or = grpc_core::JsonParse(json_string);
118   if (!json_or.ok()) {
119     gpr_log(GPR_ERROR, "JSON parsing failed: %s",
120             json_or.status().ToString().c_str());
121   } else {
122     json = std::move(*json_or);
123   }
124   return grpc_auth_refresh_token_create_from_json(json);
125 }
126 
grpc_auth_refresh_token_destruct(grpc_auth_refresh_token * refresh_token)127 void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) {
128   if (refresh_token == nullptr) return;
129   refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
130   if (refresh_token->client_id != nullptr) {
131     gpr_free(refresh_token->client_id);
132     refresh_token->client_id = nullptr;
133   }
134   if (refresh_token->client_secret != nullptr) {
135     gpr_free(refresh_token->client_secret);
136     refresh_token->client_secret = nullptr;
137   }
138   if (refresh_token->refresh_token != nullptr) {
139     gpr_free(refresh_token->refresh_token);
140     refresh_token->refresh_token = nullptr;
141   }
142 }
143 
144 //
145 // Oauth2 Token Fetcher credentials.
146 //
147 
148 grpc_oauth2_token_fetcher_credentials::
~grpc_oauth2_token_fetcher_credentials()149     ~grpc_oauth2_token_fetcher_credentials() {
150   gpr_mu_destroy(&mu_);
151   grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_));
152 }
153 
154 grpc_credentials_status
grpc_oauth2_token_fetcher_credentials_parse_server_response(const grpc_http_response * response,absl::optional<grpc_core::Slice> * token_value,grpc_core::Duration * token_lifetime)155 grpc_oauth2_token_fetcher_credentials_parse_server_response(
156     const grpc_http_response* response,
157     absl::optional<grpc_core::Slice>* token_value,
158     grpc_core::Duration* token_lifetime) {
159   char* null_terminated_body = nullptr;
160   grpc_credentials_status status = GRPC_CREDENTIALS_OK;
161 
162   if (response == nullptr) {
163     gpr_log(GPR_ERROR, "Received NULL response.");
164     status = GRPC_CREDENTIALS_ERROR;
165     goto end;
166   }
167 
168   if (response->body_length > 0) {
169     null_terminated_body =
170         static_cast<char*>(gpr_malloc(response->body_length + 1));
171     null_terminated_body[response->body_length] = '\0';
172     memcpy(null_terminated_body, response->body, response->body_length);
173   }
174 
175   if (response->status != 200) {
176     gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
177             response->status,
178             null_terminated_body != nullptr ? null_terminated_body : "");
179     status = GRPC_CREDENTIALS_ERROR;
180     goto end;
181   } else {
182     const char* access_token = nullptr;
183     const char* token_type = nullptr;
184     const char* expires_in = nullptr;
185     Json::Object::const_iterator it;
186     auto json = grpc_core::JsonParse(
187         null_terminated_body != nullptr ? null_terminated_body : "");
188     if (!json.ok()) {
189       gpr_log(GPR_ERROR, "Could not parse JSON from %s: %s",
190               null_terminated_body, json.status().ToString().c_str());
191       status = GRPC_CREDENTIALS_ERROR;
192       goto end;
193     }
194     if (json->type() != Json::Type::kObject) {
195       gpr_log(GPR_ERROR, "Response should be a JSON object");
196       status = GRPC_CREDENTIALS_ERROR;
197       goto end;
198     }
199     it = json->object().find("access_token");
200     if (it == json->object().end() ||
201         it->second.type() != Json::Type::kString) {
202       gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
203       status = GRPC_CREDENTIALS_ERROR;
204       goto end;
205     }
206     access_token = it->second.string().c_str();
207     it = json->object().find("token_type");
208     if (it == json->object().end() ||
209         it->second.type() != Json::Type::kString) {
210       gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
211       status = GRPC_CREDENTIALS_ERROR;
212       goto end;
213     }
214     token_type = it->second.string().c_str();
215     it = json->object().find("expires_in");
216     if (it == json->object().end() ||
217         it->second.type() != Json::Type::kNumber) {
218       gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
219       status = GRPC_CREDENTIALS_ERROR;
220       goto end;
221     }
222     expires_in = it->second.string().c_str();
223     *token_lifetime =
224         grpc_core::Duration::Seconds(strtol(expires_in, nullptr, 10));
225     *token_value = grpc_core::Slice::FromCopiedString(
226         absl::StrCat(token_type, " ", access_token));
227     status = GRPC_CREDENTIALS_OK;
228   }
229 
230 end:
231   if (status != GRPC_CREDENTIALS_OK) *token_value = absl::nullopt;
232   gpr_free(null_terminated_body);
233   return status;
234 }
235 
on_oauth2_token_fetcher_http_response(void * user_data,grpc_error_handle error)236 static void on_oauth2_token_fetcher_http_response(void* user_data,
237                                                   grpc_error_handle error) {
238   GRPC_LOG_IF_ERROR("oauth_fetch", error);
239   grpc_credentials_metadata_request* r =
240       static_cast<grpc_credentials_metadata_request*>(user_data);
241   grpc_oauth2_token_fetcher_credentials* c =
242       reinterpret_cast<grpc_oauth2_token_fetcher_credentials*>(r->creds.get());
243   c->on_http_response(r, error);
244 }
245 
on_http_response(grpc_credentials_metadata_request * r,grpc_error_handle error)246 void grpc_oauth2_token_fetcher_credentials::on_http_response(
247     grpc_credentials_metadata_request* r, grpc_error_handle error) {
248   absl::optional<grpc_core::Slice> access_token_value;
249   grpc_core::Duration token_lifetime;
250   grpc_credentials_status status =
251       error.ok() ? grpc_oauth2_token_fetcher_credentials_parse_server_response(
252                        &r->response, &access_token_value, &token_lifetime)
253                  : GRPC_CREDENTIALS_ERROR;
254   // Update cache and grab list of pending requests.
255   gpr_mu_lock(&mu_);
256   token_fetch_pending_ = false;
257   if (access_token_value.has_value()) {
258     access_token_value_ = access_token_value->Ref();
259   } else {
260     access_token_value_ = absl::nullopt;
261   }
262   token_expiration_ = status == GRPC_CREDENTIALS_OK
263                           ? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
264                                          token_lifetime.as_timespec())
265                           : gpr_inf_past(GPR_CLOCK_MONOTONIC);
266   grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_;
267   pending_requests_ = nullptr;
268   gpr_mu_unlock(&mu_);
269   // Invoke callbacks for all pending requests.
270   while (pending_request != nullptr) {
271     if (status == GRPC_CREDENTIALS_OK) {
272       pending_request->result = access_token_value->Ref();
273     } else {
274       auto err = GRPC_ERROR_CREATE_REFERENCING(
275           "Error occurred when fetching oauth2 token.", &error, 1);
276       pending_request->result = grpc_error_to_absl_status(err);
277     }
278     pending_request->done.store(true, std::memory_order_release);
279     pending_request->waker.Wakeup();
280     grpc_polling_entity_del_from_pollset_set(
281         pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_));
282     grpc_oauth2_pending_get_request_metadata* prev = pending_request;
283     pending_request = pending_request->next;
284     prev->Unref();
285   }
286   delete r;
287 }
288 
289 grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
GetRequestMetadata(grpc_core::ClientMetadataHandle initial_metadata,const grpc_call_credentials::GetRequestMetadataArgs *)290 grpc_oauth2_token_fetcher_credentials::GetRequestMetadata(
291     grpc_core::ClientMetadataHandle initial_metadata,
292     const grpc_call_credentials::GetRequestMetadataArgs*) {
293   // Check if we can use the cached token.
294   absl::optional<grpc_core::Slice> cached_access_token_value;
295   gpr_mu_lock(&mu_);
296   if (access_token_value_.has_value() &&
297       gpr_time_cmp(
298           gpr_time_sub(token_expiration_, gpr_now(GPR_CLOCK_MONOTONIC)),
299           gpr_time_from_seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
300                                 GPR_TIMESPAN)) > 0) {
301     cached_access_token_value = access_token_value_->Ref();
302   }
303   if (cached_access_token_value.has_value()) {
304     gpr_mu_unlock(&mu_);
305     initial_metadata->Append(
306         GRPC_AUTHORIZATION_METADATA_KEY, std::move(*cached_access_token_value),
307         [](absl::string_view, const grpc_core::Slice&) { abort(); });
308     return grpc_core::Immediate(std::move(initial_metadata));
309   }
310   // Couldn't get the token from the cache.
311   // Add request to pending_requests_ and start a new fetch if needed.
312   grpc_core::Duration refresh_threshold =
313       grpc_core::Duration::Seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS);
314   auto pending_request =
315       grpc_core::MakeRefCounted<grpc_oauth2_pending_get_request_metadata>();
316   pending_request->pollent = grpc_core::GetContext<grpc_polling_entity>();
317   pending_request->waker = grpc_core::Activity::current()->MakeNonOwningWaker();
318   grpc_polling_entity_add_to_pollset_set(
319       pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_));
320   pending_request->next = pending_requests_;
321   pending_request->md = std::move(initial_metadata);
322   pending_requests_ = pending_request->Ref().release();
323   bool start_fetch = false;
324   if (!token_fetch_pending_) {
325     token_fetch_pending_ = true;
326     start_fetch = true;
327   }
328   gpr_mu_unlock(&mu_);
329   if (start_fetch) {
330     fetch_oauth2(new grpc_credentials_metadata_request(Ref()), &pollent_,
331                  on_oauth2_token_fetcher_http_response,
332                  grpc_core::Timestamp::Now() + refresh_threshold);
333   }
334   return
335       [pending_request]()
336           -> grpc_core::Poll<absl::StatusOr<grpc_core::ClientMetadataHandle>> {
337         if (!pending_request->done.load(std::memory_order_acquire)) {
338           return grpc_core::Pending{};
339         }
340         if (pending_request->result.ok()) {
341           pending_request->md->Append(
342               GRPC_AUTHORIZATION_METADATA_KEY,
343               std::move(*pending_request->result),
344               [](absl::string_view, const grpc_core::Slice&) { abort(); });
345           return std::move(pending_request->md);
346         } else {
347           return pending_request->result.status();
348         }
349       };
350 }
351 
grpc_oauth2_token_fetcher_credentials()352 grpc_oauth2_token_fetcher_credentials::grpc_oauth2_token_fetcher_credentials()
353     : token_expiration_(gpr_inf_past(GPR_CLOCK_MONOTONIC)),
354       pollent_(grpc_polling_entity_create_from_pollset_set(
355           grpc_pollset_set_create())) {
356   gpr_mu_init(&mu_);
357 }
358 
debug_string()359 std::string grpc_oauth2_token_fetcher_credentials::debug_string() {
360   return "OAuth2TokenFetcherCredentials";
361 }
362 
type() const363 grpc_core::UniqueTypeName grpc_oauth2_token_fetcher_credentials::type() const {
364   static grpc_core::UniqueTypeName::Factory kFactory("Oauth2");
365   return kFactory.Create();
366 }
367 
368 //
369 //  Google Compute Engine credentials.
370 //
371 
372 namespace {
373 
374 class grpc_compute_engine_token_fetcher_credentials
375     : public grpc_oauth2_token_fetcher_credentials {
376  public:
377   grpc_compute_engine_token_fetcher_credentials() = default;
378   ~grpc_compute_engine_token_fetcher_credentials() override = default;
379 
380  protected:
fetch_oauth2(grpc_credentials_metadata_request * metadata_req,grpc_polling_entity * pollent,grpc_iomgr_cb_func response_cb,grpc_core::Timestamp deadline)381   void fetch_oauth2(grpc_credentials_metadata_request* metadata_req,
382                     grpc_polling_entity* pollent,
383                     grpc_iomgr_cb_func response_cb,
384                     grpc_core::Timestamp deadline) override {
385     grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
386                                const_cast<char*>("Google")};
387     grpc_http_request request;
388     memset(&request, 0, sizeof(grpc_http_request));
389     request.hdr_count = 1;
390     request.hdrs = &header;
391     // TODO(ctiller): Carry the memory quota in ctx and share it with the host
392     // channel. This would allow us to cancel an authentication query when under
393     // extreme memory pressure.
394     auto uri = grpc_core::URI::Create("http", GRPC_COMPUTE_ENGINE_METADATA_HOST,
395                                       GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH,
396                                       {} /* query params */, "" /* fragment */);
397     GPR_ASSERT(uri.ok());  // params are hardcoded
398     http_request_ = grpc_core::HttpRequest::Get(
399         std::move(*uri), nullptr /* channel args */, pollent, &request,
400         deadline,
401         GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb, metadata_req,
402                           grpc_schedule_on_exec_ctx),
403         &metadata_req->response,
404         grpc_core::RefCountedPtr<grpc_channel_credentials>(
405             grpc_insecure_credentials_create()));
406     http_request_->Start();
407   }
408 
debug_string()409   std::string debug_string() override {
410     return absl::StrFormat(
411         "GoogleComputeEngineTokenFetcherCredentials{%s}",
412         grpc_oauth2_token_fetcher_credentials::debug_string());
413   }
414 
415  private:
416   grpc_closure http_get_cb_closure_;
417   grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request_;
418 };
419 
420 }  // namespace
421 
grpc_google_compute_engine_credentials_create(void * reserved)422 grpc_call_credentials* grpc_google_compute_engine_credentials_create(
423     void* reserved) {
424   GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
425                  (reserved));
426   GPR_ASSERT(reserved == nullptr);
427   return grpc_core::MakeRefCounted<
428              grpc_compute_engine_token_fetcher_credentials>()
429       .release();
430 }
431 
432 //
433 // Google Refresh Token credentials.
434 //
435 
436 grpc_google_refresh_token_credentials::
~grpc_google_refresh_token_credentials()437     ~grpc_google_refresh_token_credentials() {
438   grpc_auth_refresh_token_destruct(&refresh_token_);
439 }
440 
fetch_oauth2(grpc_credentials_metadata_request * metadata_req,grpc_polling_entity * pollent,grpc_iomgr_cb_func response_cb,grpc_core::Timestamp deadline)441 void grpc_google_refresh_token_credentials::fetch_oauth2(
442     grpc_credentials_metadata_request* metadata_req,
443     grpc_polling_entity* pollent, grpc_iomgr_cb_func response_cb,
444     grpc_core::Timestamp deadline) {
445   grpc_http_header header = {
446       const_cast<char*>("Content-Type"),
447       const_cast<char*>("application/x-www-form-urlencoded")};
448   grpc_http_request request;
449   std::string body = absl::StrFormat(
450       GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, refresh_token_.client_id,
451       refresh_token_.client_secret, refresh_token_.refresh_token);
452   memset(&request, 0, sizeof(grpc_http_request));
453   request.hdr_count = 1;
454   request.hdrs = &header;
455   request.body = const_cast<char*>(body.c_str());
456   request.body_length = body.size();
457   // TODO(ctiller): Carry the memory quota in ctx and share it with the host
458   // channel. This would allow us to cancel an authentication query when under
459   // extreme memory pressure.
460   auto uri = grpc_core::URI::Create("https", GRPC_GOOGLE_OAUTH2_SERVICE_HOST,
461                                     GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH,
462                                     {} /* query params */, "" /* fragment */);
463   GPR_ASSERT(uri.ok());  // params are hardcoded
464   http_request_ = grpc_core::HttpRequest::Post(
465       std::move(*uri), nullptr /* channel args */, pollent, &request, deadline,
466       GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
467                         grpc_schedule_on_exec_ctx),
468       &metadata_req->response, grpc_core::CreateHttpRequestSSLCredentials());
469   http_request_->Start();
470 }
471 
grpc_google_refresh_token_credentials(grpc_auth_refresh_token refresh_token)472 grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials(
473     grpc_auth_refresh_token refresh_token)
474     : refresh_token_(refresh_token) {}
475 
476 grpc_core::RefCountedPtr<grpc_call_credentials>
grpc_refresh_token_credentials_create_from_auth_refresh_token(grpc_auth_refresh_token refresh_token)477 grpc_refresh_token_credentials_create_from_auth_refresh_token(
478     grpc_auth_refresh_token refresh_token) {
479   if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
480     gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
481     return nullptr;
482   }
483   return grpc_core::MakeRefCounted<grpc_google_refresh_token_credentials>(
484       refresh_token);
485 }
486 
debug_string()487 std::string grpc_google_refresh_token_credentials::debug_string() {
488   return absl::StrFormat("GoogleRefreshToken{ClientID:%s,%s}",
489                          refresh_token_.client_id,
490                          grpc_oauth2_token_fetcher_credentials::debug_string());
491 }
492 
type() const493 grpc_core::UniqueTypeName grpc_google_refresh_token_credentials::type() const {
494   static grpc_core::UniqueTypeName::Factory kFactory("GoogleRefreshToken");
495   return kFactory.Create();
496 }
497 
create_loggable_refresh_token(grpc_auth_refresh_token * token)498 static std::string create_loggable_refresh_token(
499     grpc_auth_refresh_token* token) {
500   if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) {
501     return "<Invalid json token>";
502   }
503   return absl::StrFormat(
504       "{\n type: %s\n client_id: %s\n client_secret: "
505       "<redacted>\n refresh_token: <redacted>\n}",
506       token->type, token->client_id);
507 }
508 
grpc_google_refresh_token_credentials_create(const char * json_refresh_token,void * reserved)509 grpc_call_credentials* grpc_google_refresh_token_credentials_create(
510     const char* json_refresh_token, void* reserved) {
511   grpc_auth_refresh_token token =
512       grpc_auth_refresh_token_create_from_string(json_refresh_token);
513   if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {
514     gpr_log(GPR_INFO,
515             "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
516             "reserved=%p)",
517             create_loggable_refresh_token(&token).c_str(), reserved);
518   }
519   GPR_ASSERT(reserved == nullptr);
520   return grpc_refresh_token_credentials_create_from_auth_refresh_token(token)
521       .release();
522 }
523 
524 //
525 // STS credentials.
526 //
527 
528 namespace grpc_core {
529 
530 namespace {
531 
MaybeAddToBody(const char * field_name,const char * field,std::vector<std::string> * body)532 void MaybeAddToBody(const char* field_name, const char* field,
533                     std::vector<std::string>* body) {
534   if (field == nullptr || strlen(field) == 0) return;
535   body->push_back(absl::StrFormat("&%s=%s", field_name, field));
536 }
537 
LoadTokenFile(const char * path,grpc_slice * token)538 grpc_error_handle LoadTokenFile(const char* path, grpc_slice* token) {
539   grpc_error_handle err = grpc_load_file(path, 1, token);
540   if (!err.ok()) return err;
541   if (GRPC_SLICE_LENGTH(*token) == 0) {
542     gpr_log(GPR_ERROR, "Token file %s is empty", path);
543     err = GRPC_ERROR_CREATE("Token file is empty.");
544   }
545   return err;
546 }
547 
548 class StsTokenFetcherCredentials
549     : public grpc_oauth2_token_fetcher_credentials {
550  public:
StsTokenFetcherCredentials(URI sts_url,const grpc_sts_credentials_options * options)551   StsTokenFetcherCredentials(URI sts_url,
552                              const grpc_sts_credentials_options* options)
553       : sts_url_(std::move(sts_url)),
554         resource_(gpr_strdup(options->resource)),
555         audience_(gpr_strdup(options->audience)),
556         scope_(gpr_strdup(options->scope)),
557         requested_token_type_(gpr_strdup(options->requested_token_type)),
558         subject_token_path_(gpr_strdup(options->subject_token_path)),
559         subject_token_type_(gpr_strdup(options->subject_token_type)),
560         actor_token_path_(gpr_strdup(options->actor_token_path)),
561         actor_token_type_(gpr_strdup(options->actor_token_type)) {}
562 
debug_string()563   std::string debug_string() override {
564     return absl::StrFormat(
565         "StsTokenFetcherCredentials{Path:%s,Authority:%s,%s}", sts_url_.path(),
566         sts_url_.authority(),
567         grpc_oauth2_token_fetcher_credentials::debug_string());
568   }
569 
570  private:
fetch_oauth2(grpc_credentials_metadata_request * metadata_req,grpc_polling_entity * pollent,grpc_iomgr_cb_func response_cb,Timestamp deadline)571   void fetch_oauth2(grpc_credentials_metadata_request* metadata_req,
572                     grpc_polling_entity* pollent,
573                     grpc_iomgr_cb_func response_cb,
574                     Timestamp deadline) override {
575     grpc_http_request request;
576     memset(&request, 0, sizeof(grpc_http_request));
577     grpc_error_handle err = FillBody(&request.body, &request.body_length);
578     if (!err.ok()) {
579       response_cb(metadata_req, err);
580       return;
581     }
582     grpc_http_header header = {
583         const_cast<char*>("Content-Type"),
584         const_cast<char*>("application/x-www-form-urlencoded")};
585     request.hdr_count = 1;
586     request.hdrs = &header;
587     // TODO(ctiller): Carry the memory quota in ctx and share it with the host
588     // channel. This would allow us to cancel an authentication query when under
589     // extreme memory pressure.
590     RefCountedPtr<grpc_channel_credentials> http_request_creds;
591     if (sts_url_.scheme() == "http") {
592       http_request_creds = RefCountedPtr<grpc_channel_credentials>(
593           grpc_insecure_credentials_create());
594     } else {
595       http_request_creds = CreateHttpRequestSSLCredentials();
596     }
597     http_request_ = HttpRequest::Post(
598         sts_url_, nullptr /* channel args */, pollent, &request, deadline,
599         GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
600                           grpc_schedule_on_exec_ctx),
601         &metadata_req->response, std::move(http_request_creds));
602     http_request_->Start();
603     gpr_free(request.body);
604   }
605 
FillBody(char ** body,size_t * body_length)606   grpc_error_handle FillBody(char** body, size_t* body_length) {
607     *body = nullptr;
608     std::vector<std::string> body_parts;
609     grpc_slice subject_token = grpc_empty_slice();
610     grpc_slice actor_token = grpc_empty_slice();
611     grpc_error_handle err;
612 
613     auto cleanup = [&body, &body_length, &body_parts, &subject_token,
614                     &actor_token, &err]() {
615       if (err.ok()) {
616         std::string body_str = absl::StrJoin(body_parts, "");
617         *body = gpr_strdup(body_str.c_str());
618         *body_length = body_str.size();
619       }
620       CSliceUnref(subject_token);
621       CSliceUnref(actor_token);
622       return err;
623     };
624 
625     err = LoadTokenFile(subject_token_path_.get(), &subject_token);
626     if (!err.ok()) return cleanup();
627     body_parts.push_back(absl::StrFormat(
628         GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING,
629         reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(subject_token)),
630         subject_token_type_.get()));
631     MaybeAddToBody("resource", resource_.get(), &body_parts);
632     MaybeAddToBody("audience", audience_.get(), &body_parts);
633     MaybeAddToBody("scope", scope_.get(), &body_parts);
634     MaybeAddToBody("requested_token_type", requested_token_type_.get(),
635                    &body_parts);
636     if ((actor_token_path_ != nullptr) && *actor_token_path_ != '\0') {
637       err = LoadTokenFile(actor_token_path_.get(), &actor_token);
638       if (!err.ok()) return cleanup();
639       MaybeAddToBody(
640           "actor_token",
641           reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(actor_token)),
642           &body_parts);
643       MaybeAddToBody("actor_token_type", actor_token_type_.get(), &body_parts);
644     }
645     return cleanup();
646   }
647 
648   URI sts_url_;
649   grpc_closure http_post_cb_closure_;
650   UniquePtr<char> resource_;
651   UniquePtr<char> audience_;
652   UniquePtr<char> scope_;
653   UniquePtr<char> requested_token_type_;
654   UniquePtr<char> subject_token_path_;
655   UniquePtr<char> subject_token_type_;
656   UniquePtr<char> actor_token_path_;
657   UniquePtr<char> actor_token_type_;
658   OrphanablePtr<HttpRequest> http_request_;
659 };
660 
661 }  // namespace
662 
ValidateStsCredentialsOptions(const grpc_sts_credentials_options * options)663 absl::StatusOr<URI> ValidateStsCredentialsOptions(
664     const grpc_sts_credentials_options* options) {
665   std::vector<grpc_error_handle> error_list;
666   absl::StatusOr<URI> sts_url =
667       URI::Parse(options->token_exchange_service_uri == nullptr
668                      ? ""
669                      : options->token_exchange_service_uri);
670   if (!sts_url.ok()) {
671     error_list.push_back(GRPC_ERROR_CREATE(
672         absl::StrFormat("Invalid or missing STS endpoint URL. Error: %s",
673                         sts_url.status().ToString())));
674   } else if (sts_url->scheme() != "https" && sts_url->scheme() != "http") {
675     error_list.push_back(
676         GRPC_ERROR_CREATE("Invalid URI scheme, must be https to http."));
677   }
678   if (options->subject_token_path == nullptr ||
679       strlen(options->subject_token_path) == 0) {
680     error_list.push_back(
681         GRPC_ERROR_CREATE("subject_token needs to be specified"));
682   }
683   if (options->subject_token_type == nullptr ||
684       strlen(options->subject_token_type) == 0) {
685     error_list.push_back(
686         GRPC_ERROR_CREATE("subject_token_type needs to be specified"));
687   }
688   if (error_list.empty()) {
689     return sts_url;
690   }
691   auto grpc_error_vec = GRPC_ERROR_CREATE_FROM_VECTOR(
692       "Invalid STS Credentials Options", &error_list);
693   auto retval = absl::InvalidArgumentError(StatusToString(grpc_error_vec));
694   return retval;
695 }
696 
697 }  // namespace grpc_core
698 
grpc_sts_credentials_create(const grpc_sts_credentials_options * options,void * reserved)699 grpc_call_credentials* grpc_sts_credentials_create(
700     const grpc_sts_credentials_options* options, void* reserved) {
701   GPR_ASSERT(reserved == nullptr);
702   absl::StatusOr<grpc_core::URI> sts_url =
703       grpc_core::ValidateStsCredentialsOptions(options);
704   if (!sts_url.ok()) {
705     gpr_log(GPR_ERROR, "STS Credentials creation failed. Error: %s.",
706             sts_url.status().ToString().c_str());
707     return nullptr;
708   }
709   return grpc_core::MakeRefCounted<grpc_core::StsTokenFetcherCredentials>(
710              std::move(*sts_url), options)
711       .release();
712 }
713 
714 //
715 // Oauth2 Access Token credentials.
716 //
717 
718 grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
GetRequestMetadata(grpc_core::ClientMetadataHandle initial_metadata,const grpc_call_credentials::GetRequestMetadataArgs *)719 grpc_access_token_credentials::GetRequestMetadata(
720     grpc_core::ClientMetadataHandle initial_metadata,
721     const grpc_call_credentials::GetRequestMetadataArgs*) {
722   initial_metadata->Append(
723       GRPC_AUTHORIZATION_METADATA_KEY, access_token_value_.Ref(),
724       [](absl::string_view, const grpc_core::Slice&) { abort(); });
725   return grpc_core::Immediate(std::move(initial_metadata));
726 }
727 
Type()728 grpc_core::UniqueTypeName grpc_access_token_credentials::Type() {
729   static grpc_core::UniqueTypeName::Factory kFactory("AccessToken");
730   return kFactory.Create();
731 }
732 
grpc_access_token_credentials(const char * access_token)733 grpc_access_token_credentials::grpc_access_token_credentials(
734     const char* access_token)
735     : access_token_value_(grpc_core::Slice::FromCopiedString(
736           absl::StrCat("Bearer ", access_token))) {}
737 
debug_string()738 std::string grpc_access_token_credentials::debug_string() {
739   return "AccessTokenCredentials{Token:present}";
740 }
741 
grpc_access_token_credentials_create(const char * access_token,void * reserved)742 grpc_call_credentials* grpc_access_token_credentials_create(
743     const char* access_token, void* reserved) {
744   GRPC_API_TRACE(
745       "grpc_access_token_credentials_create(access_token=<redacted>, "
746       "reserved=%p)",
747       1, (reserved));
748   GPR_ASSERT(reserved == nullptr);
749   return grpc_core::MakeRefCounted<grpc_access_token_credentials>(access_token)
750       .release();
751 }
752