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