| /* |
| * |
| * Copyright 2016 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include "src/core/lib/security/credentials/jwt/jwt_credentials.h" |
| |
| #include <inttypes.h> |
| #include <string.h> |
| |
| #include "src/core/lib/gprpp/ref_counted.h" |
| #include "src/core/lib/gprpp/ref_counted_ptr.h" |
| #include "src/core/lib/surface/api_trace.h" |
| |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| #include <grpc/support/sync.h> |
| |
| void grpc_service_account_jwt_access_credentials::reset_cache() { |
| GRPC_MDELEM_UNREF(cached_.jwt_md); |
| cached_.jwt_md = GRPC_MDNULL; |
| if (cached_.service_url != nullptr) { |
| gpr_free(cached_.service_url); |
| cached_.service_url = nullptr; |
| } |
| cached_.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); |
| } |
| |
| grpc_service_account_jwt_access_credentials:: |
| ~grpc_service_account_jwt_access_credentials() { |
| grpc_auth_json_key_destruct(&key_); |
| reset_cache(); |
| gpr_mu_destroy(&cache_mu_); |
| } |
| |
| bool grpc_service_account_jwt_access_credentials::get_request_metadata( |
| grpc_polling_entity* pollent, grpc_auth_metadata_context context, |
| grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, |
| grpc_error** error) { |
| gpr_timespec refresh_threshold = gpr_time_from_seconds( |
| GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); |
| |
| /* See if we can return a cached jwt. */ |
| grpc_mdelem jwt_md = GRPC_MDNULL; |
| { |
| gpr_mu_lock(&cache_mu_); |
| if (cached_.service_url != nullptr && |
| strcmp(cached_.service_url, context.service_url) == 0 && |
| !GRPC_MDISNULL(cached_.jwt_md) && |
| (gpr_time_cmp( |
| gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)), |
| refresh_threshold) > 0)) { |
| jwt_md = GRPC_MDELEM_REF(cached_.jwt_md); |
| } |
| gpr_mu_unlock(&cache_mu_); |
| } |
| |
| if (GRPC_MDISNULL(jwt_md)) { |
| char* jwt = nullptr; |
| /* Generate a new jwt. */ |
| gpr_mu_lock(&cache_mu_); |
| reset_cache(); |
| jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_, |
| nullptr); |
| if (jwt != nullptr) { |
| char* md_value; |
| gpr_asprintf(&md_value, "Bearer %s", jwt); |
| gpr_free(jwt); |
| cached_.jwt_expiration = |
| gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_); |
| cached_.service_url = gpr_strdup(context.service_url); |
| cached_.jwt_md = grpc_mdelem_from_slices( |
| grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), |
| grpc_slice_from_copied_string(md_value)); |
| gpr_free(md_value); |
| jwt_md = GRPC_MDELEM_REF(cached_.jwt_md); |
| } |
| gpr_mu_unlock(&cache_mu_); |
| } |
| |
| if (!GRPC_MDISNULL(jwt_md)) { |
| grpc_credentials_mdelem_array_add(md_array, jwt_md); |
| GRPC_MDELEM_UNREF(jwt_md); |
| } else { |
| *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT."); |
| } |
| return true; |
| } |
| |
| void grpc_service_account_jwt_access_credentials::cancel_get_request_metadata( |
| grpc_credentials_mdelem_array* md_array, grpc_error* error) { |
| GRPC_ERROR_UNREF(error); |
| } |
| |
| grpc_service_account_jwt_access_credentials:: |
| grpc_service_account_jwt_access_credentials(grpc_auth_json_key key, |
| gpr_timespec token_lifetime) |
| : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT), key_(key) { |
| gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime(); |
| if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) { |
| gpr_log(GPR_INFO, |
| "Cropping token lifetime to maximum allowed value (%d secs).", |
| static_cast<int>(max_token_lifetime.tv_sec)); |
| token_lifetime = grpc_max_auth_token_lifetime(); |
| } |
| jwt_lifetime_ = token_lifetime; |
| gpr_mu_init(&cache_mu_); |
| reset_cache(); |
| } |
| |
| grpc_core::RefCountedPtr<grpc_call_credentials> |
| grpc_service_account_jwt_access_credentials_create_from_auth_json_key( |
| grpc_auth_json_key key, gpr_timespec token_lifetime) { |
| if (!grpc_auth_json_key_is_valid(&key)) { |
| gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); |
| return nullptr; |
| } |
| return grpc_core::MakeRefCounted<grpc_service_account_jwt_access_credentials>( |
| key, token_lifetime); |
| } |
| |
| static char* redact_private_key(const char* json_key) { |
| char* json_copy = gpr_strdup(json_key); |
| grpc_json* json = grpc_json_parse_string(json_copy); |
| if (!json) { |
| gpr_free(json_copy); |
| return gpr_strdup("<Json failed to parse.>"); |
| } |
| const char* redacted = "<redacted>"; |
| grpc_json* current = json->child; |
| while (current) { |
| if (current->type == GRPC_JSON_STRING && |
| strcmp(current->key, "private_key") == 0) { |
| current->value = const_cast<char*>(redacted); |
| break; |
| } |
| current = current->next; |
| } |
| char* clean_json = grpc_json_dump_to_string(json, 2); |
| gpr_free(json_copy); |
| grpc_json_destroy(json); |
| return clean_json; |
| } |
| |
| grpc_call_credentials* grpc_service_account_jwt_access_credentials_create( |
| const char* json_key, gpr_timespec token_lifetime, void* reserved) { |
| if (grpc_api_trace.enabled()) { |
| char* clean_json = redact_private_key(json_key); |
| gpr_log(GPR_INFO, |
| "grpc_service_account_jwt_access_credentials_create(" |
| "json_key=%s, " |
| "token_lifetime=" |
| "gpr_timespec { tv_sec: %" PRId64 |
| ", tv_nsec: %d, clock_type: %d }, " |
| "reserved=%p)", |
| clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec, |
| static_cast<int>(token_lifetime.clock_type), reserved); |
| gpr_free(clean_json); |
| } |
| GPR_ASSERT(reserved == nullptr); |
| grpc_core::ExecCtx exec_ctx; |
| return grpc_service_account_jwt_access_credentials_create_from_auth_json_key( |
| grpc_auth_json_key_create_from_string(json_key), token_lifetime) |
| .release(); |
| } |