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