1 /*
2 *
3 * Copyright 2016 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/jwt/jwt_credentials.h"
22
23 #include <inttypes.h>
24 #include <string.h>
25
26 #include <string>
27
28 #include "absl/strings/str_cat.h"
29
30 #include "src/core/lib/gprpp/ref_counted.h"
31 #include "src/core/lib/gprpp/ref_counted_ptr.h"
32 #include "src/core/lib/slice/slice_internal.h"
33 #include "src/core/lib/surface/api_trace.h"
34
35 #include <grpc/support/alloc.h>
36 #include <grpc/support/log.h>
37 #include <grpc/support/string_util.h>
38 #include <grpc/support/sync.h>
39
40 using grpc_core::Json;
41
reset_cache()42 void grpc_service_account_jwt_access_credentials::reset_cache() {
43 GRPC_MDELEM_UNREF(cached_.jwt_md);
44 cached_.jwt_md = GRPC_MDNULL;
45 if (cached_.service_url != nullptr) {
46 gpr_free(cached_.service_url);
47 cached_.service_url = nullptr;
48 }
49 cached_.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
50 }
51
52 grpc_service_account_jwt_access_credentials::
~grpc_service_account_jwt_access_credentials()53 ~grpc_service_account_jwt_access_credentials() {
54 grpc_auth_json_key_destruct(&key_);
55 reset_cache();
56 gpr_mu_destroy(&cache_mu_);
57 }
58
get_request_metadata(grpc_polling_entity *,grpc_auth_metadata_context context,grpc_credentials_mdelem_array * md_array,grpc_closure *,grpc_error ** error)59 bool grpc_service_account_jwt_access_credentials::get_request_metadata(
60 grpc_polling_entity* /*pollent*/, grpc_auth_metadata_context context,
61 grpc_credentials_mdelem_array* md_array,
62 grpc_closure* /*on_request_metadata*/, grpc_error** error) {
63 gpr_timespec refresh_threshold = gpr_time_from_seconds(
64 GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
65
66 /* See if we can return a cached jwt. */
67 grpc_mdelem jwt_md = GRPC_MDNULL;
68 {
69 gpr_mu_lock(&cache_mu_);
70 if (cached_.service_url != nullptr &&
71 strcmp(cached_.service_url, context.service_url) == 0 &&
72 !GRPC_MDISNULL(cached_.jwt_md) &&
73 (gpr_time_cmp(
74 gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)),
75 refresh_threshold) > 0)) {
76 jwt_md = GRPC_MDELEM_REF(cached_.jwt_md);
77 }
78 gpr_mu_unlock(&cache_mu_);
79 }
80
81 if (GRPC_MDISNULL(jwt_md)) {
82 char* jwt = nullptr;
83 /* Generate a new jwt. */
84 gpr_mu_lock(&cache_mu_);
85 reset_cache();
86 jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_,
87 nullptr);
88 if (jwt != nullptr) {
89 std::string md_value = absl::StrCat("Bearer ", jwt);
90 gpr_free(jwt);
91 cached_.jwt_expiration =
92 gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_);
93 cached_.service_url = gpr_strdup(context.service_url);
94 cached_.jwt_md = grpc_mdelem_from_slices(
95 grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
96 grpc_slice_from_cpp_string(std::move(md_value)));
97 jwt_md = GRPC_MDELEM_REF(cached_.jwt_md);
98 }
99 gpr_mu_unlock(&cache_mu_);
100 }
101
102 if (!GRPC_MDISNULL(jwt_md)) {
103 grpc_credentials_mdelem_array_add(md_array, jwt_md);
104 GRPC_MDELEM_UNREF(jwt_md);
105 } else {
106 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT.");
107 }
108 return true;
109 }
110
cancel_get_request_metadata(grpc_credentials_mdelem_array *,grpc_error * error)111 void grpc_service_account_jwt_access_credentials::cancel_get_request_metadata(
112 grpc_credentials_mdelem_array* /*md_array*/, grpc_error* error) {
113 GRPC_ERROR_UNREF(error);
114 }
115
116 grpc_service_account_jwt_access_credentials::
grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,gpr_timespec token_lifetime)117 grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
118 gpr_timespec token_lifetime)
119 : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT), key_(key) {
120 gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime();
121 if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) {
122 gpr_log(GPR_INFO,
123 "Cropping token lifetime to maximum allowed value (%d secs).",
124 static_cast<int>(max_token_lifetime.tv_sec));
125 token_lifetime = grpc_max_auth_token_lifetime();
126 }
127 jwt_lifetime_ = token_lifetime;
128 gpr_mu_init(&cache_mu_);
129 reset_cache();
130 }
131
132 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)133 grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
134 grpc_auth_json_key key, gpr_timespec token_lifetime) {
135 if (!grpc_auth_json_key_is_valid(&key)) {
136 gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
137 return nullptr;
138 }
139 return grpc_core::MakeRefCounted<grpc_service_account_jwt_access_credentials>(
140 key, token_lifetime);
141 }
142
redact_private_key(const char * json_key)143 static char* redact_private_key(const char* json_key) {
144 grpc_error* error = GRPC_ERROR_NONE;
145 Json json = Json::Parse(json_key, &error);
146 if (error != GRPC_ERROR_NONE || json.type() != Json::Type::OBJECT) {
147 GRPC_ERROR_UNREF(error);
148 return gpr_strdup("<Json failed to parse.>");
149 }
150 (*json.mutable_object())["private_key"] = "<redacted>";
151 return gpr_strdup(json.Dump(/*indent=*/2).c_str());
152 }
153
grpc_service_account_jwt_access_credentials_create(const char * json_key,gpr_timespec token_lifetime,void * reserved)154 grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
155 const char* json_key, gpr_timespec token_lifetime, void* reserved) {
156 if (GRPC_TRACE_FLAG_ENABLED(grpc_api_trace)) {
157 char* clean_json = redact_private_key(json_key);
158 gpr_log(GPR_INFO,
159 "grpc_service_account_jwt_access_credentials_create("
160 "json_key=%s, "
161 "token_lifetime="
162 "gpr_timespec { tv_sec: %" PRId64
163 ", tv_nsec: %d, clock_type: %d }, "
164 "reserved=%p)",
165 clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec,
166 static_cast<int>(token_lifetime.clock_type), reserved);
167 gpr_free(clean_json);
168 }
169 GPR_ASSERT(reserved == nullptr);
170 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
171 grpc_core::ExecCtx exec_ctx;
172 return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
173 grpc_auth_json_key_create_from_string(json_key), token_lifetime)
174 .release();
175 }
176