• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/surface/api_trace.h"
27 
28 #include <grpc/support/alloc.h>
29 #include <grpc/support/log.h>
30 #include <grpc/support/string_util.h>
31 #include <grpc/support/sync.h>
32 
jwt_reset_cache(grpc_service_account_jwt_access_credentials * c)33 static void jwt_reset_cache(grpc_service_account_jwt_access_credentials* c) {
34   GRPC_MDELEM_UNREF(c->cached.jwt_md);
35   c->cached.jwt_md = GRPC_MDNULL;
36   if (c->cached.service_url != nullptr) {
37     gpr_free(c->cached.service_url);
38     c->cached.service_url = nullptr;
39   }
40   c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
41 }
42 
jwt_destruct(grpc_call_credentials * creds)43 static void jwt_destruct(grpc_call_credentials* creds) {
44   grpc_service_account_jwt_access_credentials* c =
45       reinterpret_cast<grpc_service_account_jwt_access_credentials*>(creds);
46   grpc_auth_json_key_destruct(&c->key);
47   jwt_reset_cache(c);
48   gpr_mu_destroy(&c->cache_mu);
49 }
50 
jwt_get_request_metadata(grpc_call_credentials * creds,grpc_polling_entity * pollent,grpc_auth_metadata_context context,grpc_credentials_mdelem_array * md_array,grpc_closure * on_request_metadata,grpc_error ** error)51 static bool jwt_get_request_metadata(grpc_call_credentials* creds,
52                                      grpc_polling_entity* pollent,
53                                      grpc_auth_metadata_context context,
54                                      grpc_credentials_mdelem_array* md_array,
55                                      grpc_closure* on_request_metadata,
56                                      grpc_error** error) {
57   grpc_service_account_jwt_access_credentials* c =
58       reinterpret_cast<grpc_service_account_jwt_access_credentials*>(creds);
59   gpr_timespec refresh_threshold = gpr_time_from_seconds(
60       GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
61 
62   /* See if we can return a cached jwt. */
63   grpc_mdelem jwt_md = GRPC_MDNULL;
64   {
65     gpr_mu_lock(&c->cache_mu);
66     if (c->cached.service_url != nullptr &&
67         strcmp(c->cached.service_url, context.service_url) == 0 &&
68         !GRPC_MDISNULL(c->cached.jwt_md) &&
69         (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration,
70                                    gpr_now(GPR_CLOCK_REALTIME)),
71                       refresh_threshold) > 0)) {
72       jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md);
73     }
74     gpr_mu_unlock(&c->cache_mu);
75   }
76 
77   if (GRPC_MDISNULL(jwt_md)) {
78     char* jwt = nullptr;
79     /* Generate a new jwt. */
80     gpr_mu_lock(&c->cache_mu);
81     jwt_reset_cache(c);
82     jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url,
83                                    c->jwt_lifetime, nullptr);
84     if (jwt != nullptr) {
85       char* md_value;
86       gpr_asprintf(&md_value, "Bearer %s", jwt);
87       gpr_free(jwt);
88       c->cached.jwt_expiration =
89           gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime);
90       c->cached.service_url = gpr_strdup(context.service_url);
91       c->cached.jwt_md = grpc_mdelem_from_slices(
92           grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY),
93           grpc_slice_from_copied_string(md_value));
94       gpr_free(md_value);
95       jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md);
96     }
97     gpr_mu_unlock(&c->cache_mu);
98   }
99 
100   if (!GRPC_MDISNULL(jwt_md)) {
101     grpc_credentials_mdelem_array_add(md_array, jwt_md);
102     GRPC_MDELEM_UNREF(jwt_md);
103   } else {
104     *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT.");
105   }
106   return true;
107 }
108 
jwt_cancel_get_request_metadata(grpc_call_credentials * c,grpc_credentials_mdelem_array * md_array,grpc_error * error)109 static void jwt_cancel_get_request_metadata(
110     grpc_call_credentials* c, grpc_credentials_mdelem_array* md_array,
111     grpc_error* error) {
112   GRPC_ERROR_UNREF(error);
113 }
114 
115 static grpc_call_credentials_vtable jwt_vtable = {
116     jwt_destruct, jwt_get_request_metadata, jwt_cancel_get_request_metadata};
117 
118 grpc_call_credentials*
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(grpc_auth_json_key key,gpr_timespec token_lifetime)119 grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
120     grpc_auth_json_key key, gpr_timespec token_lifetime) {
121   grpc_service_account_jwt_access_credentials* c;
122   if (!grpc_auth_json_key_is_valid(&key)) {
123     gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
124     return nullptr;
125   }
126   c = static_cast<grpc_service_account_jwt_access_credentials*>(
127       gpr_zalloc(sizeof(grpc_service_account_jwt_access_credentials)));
128   c->base.type = GRPC_CALL_CREDENTIALS_TYPE_JWT;
129   gpr_ref_init(&c->base.refcount, 1);
130   c->base.vtable = &jwt_vtable;
131   c->key = key;
132   gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime();
133   if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) {
134     gpr_log(GPR_INFO,
135             "Cropping token lifetime to maximum allowed value (%d secs).",
136             static_cast<int>(max_token_lifetime.tv_sec));
137     token_lifetime = grpc_max_auth_token_lifetime();
138   }
139   c->jwt_lifetime = token_lifetime;
140   gpr_mu_init(&c->cache_mu);
141   jwt_reset_cache(c);
142   return &c->base;
143 }
144 
redact_private_key(const char * json_key)145 static char* redact_private_key(const char* json_key) {
146   char* json_copy = gpr_strdup(json_key);
147   grpc_json* json = grpc_json_parse_string(json_copy);
148   if (!json) {
149     gpr_free(json_copy);
150     return gpr_strdup("<Json failed to parse.>");
151   }
152   const char* redacted = "<redacted>";
153   grpc_json* current = json->child;
154   while (current) {
155     if (current->type == GRPC_JSON_STRING &&
156         strcmp(current->key, "private_key") == 0) {
157       current->value = const_cast<char*>(redacted);
158       break;
159     }
160     current = current->next;
161   }
162   char* clean_json = grpc_json_dump_to_string(json, 2);
163   gpr_free(json_copy);
164   grpc_json_destroy(json);
165   return clean_json;
166 }
167 
grpc_service_account_jwt_access_credentials_create(const char * json_key,gpr_timespec token_lifetime,void * reserved)168 grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
169     const char* json_key, gpr_timespec token_lifetime, void* reserved) {
170   if (grpc_api_trace.enabled()) {
171     char* clean_json = redact_private_key(json_key);
172     gpr_log(GPR_INFO,
173             "grpc_service_account_jwt_access_credentials_create("
174             "json_key=%s, "
175             "token_lifetime="
176             "gpr_timespec { tv_sec: %" PRId64
177             ", tv_nsec: %d, clock_type: %d }, "
178             "reserved=%p)",
179             clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec,
180             static_cast<int>(token_lifetime.clock_type), reserved);
181     gpr_free(clean_json);
182   }
183   GPR_ASSERT(reserved == nullptr);
184   grpc_core::ExecCtx exec_ctx;
185   grpc_call_credentials* creds =
186       grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
187           grpc_auth_json_key_create_from_string(json_key), token_lifetime);
188 
189   return creds;
190 }
191