• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2015 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 "src/core/lib/security/credentials/jwt/json_token.h"
20 
21 #include <grpc/credentials.h>
22 #include <grpc/grpc_security.h>
23 #include <grpc/support/alloc.h>
24 #include <grpc/support/json.h>
25 #include <grpc/support/port_platform.h>
26 #include <grpc/support/string_util.h>
27 #include <grpc/support/time.h>
28 #include <openssl/bio.h>
29 #include <openssl/evp.h>
30 #include <openssl/pem.h>
31 #include <openssl/rsa.h>
32 #include <stdint.h>
33 #include <string.h>
34 
35 #include <string>
36 #include <utility>
37 
38 #include "absl/log/check.h"
39 #include "absl/log/log.h"
40 #include "absl/status/status.h"
41 #include "absl/status/statusor.h"
42 #include "absl/strings/escaping.h"
43 #include "src/core/lib/iomgr/error.h"
44 #include "src/core/lib/security/util/json_util.h"
45 #include "src/core/util/json/json_reader.h"
46 #include "src/core/util/json/json_writer.h"
47 
48 using grpc_core::Json;
49 
50 // --- Constants. ---
51 
52 // 1 hour max.
grpc_max_auth_token_lifetime()53 gpr_timespec grpc_max_auth_token_lifetime() {
54   gpr_timespec out;
55   out.tv_sec = 3600;
56   out.tv_nsec = 0;
57   out.clock_type = GPR_TIMESPAN;
58   return out;
59 }
60 
61 #define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256"
62 #define GRPC_JWT_TYPE "JWT"
63 
64 // --- Override for testing. ---
65 
66 static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override =
67     nullptr;
68 
69 // --- grpc_auth_json_key. ---
70 
grpc_auth_json_key_is_valid(const grpc_auth_json_key * json_key)71 int grpc_auth_json_key_is_valid(const grpc_auth_json_key* json_key) {
72   return (json_key != nullptr) &&
73          strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID) != 0;
74 }
75 
grpc_auth_json_key_create_from_json(const Json & json)76 grpc_auth_json_key grpc_auth_json_key_create_from_json(const Json& json) {
77   grpc_auth_json_key result;
78   BIO* bio = nullptr;
79   const char* prop_value;
80   int success = 0;
81   grpc_error_handle error;
82 
83   memset(&result, 0, sizeof(grpc_auth_json_key));
84   result.type = GRPC_AUTH_JSON_TYPE_INVALID;
85   if (json.type() == Json::Type::kNull) {
86     LOG(ERROR) << "Invalid json.";
87     goto end;
88   }
89 
90   prop_value = grpc_json_get_string_property(json, "type", &error);
91   GRPC_LOG_IF_ERROR("JSON key parsing", error);
92   if (prop_value == nullptr ||
93       strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT) != 0) {
94     goto end;
95   }
96   result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT;
97 
98   if (!grpc_copy_json_string_property(json, "private_key_id",
99                                       &result.private_key_id) ||
100       !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
101       !grpc_copy_json_string_property(json, "client_email",
102                                       &result.client_email)) {
103     goto end;
104   }
105 
106   prop_value = grpc_json_get_string_property(json, "private_key", &error);
107   GRPC_LOG_IF_ERROR("JSON key parsing", error);
108   if (prop_value == nullptr) {
109     goto end;
110   }
111   bio = BIO_new(BIO_s_mem());
112   success = BIO_puts(bio, prop_value);
113   if ((success < 0) || (static_cast<size_t>(success) != strlen(prop_value))) {
114     LOG(ERROR) << "Could not write into openssl BIO.";
115     goto end;
116   }
117 #if OPENSSL_VERSION_NUMBER < 0x30000000L
118   result.private_key =
119       PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, const_cast<char*>(""));
120 #else
121   result.private_key = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
122 #endif
123   if (result.private_key == nullptr) {
124     LOG(ERROR) << "Could not deserialize private key.";
125     goto end;
126   }
127   success = 1;
128 
129 end:
130   if (bio != nullptr) BIO_free(bio);
131   if (!success) grpc_auth_json_key_destruct(&result);
132   return result;
133 }
134 
grpc_auth_json_key_create_from_string(const char * json_string)135 grpc_auth_json_key grpc_auth_json_key_create_from_string(
136     const char* json_string) {
137   Json json;
138   auto json_or = grpc_core::JsonParse(json_string);
139   if (!json_or.ok()) {
140     LOG(ERROR) << "JSON key parsing error: " << json_or.status();
141   } else {
142     json = std::move(*json_or);
143   }
144   return grpc_auth_json_key_create_from_json(json);
145 }
146 
grpc_auth_json_key_destruct(grpc_auth_json_key * json_key)147 void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key) {
148   if (json_key == nullptr) return;
149   json_key->type = GRPC_AUTH_JSON_TYPE_INVALID;
150   if (json_key->client_id != nullptr) {
151     gpr_free(json_key->client_id);
152     json_key->client_id = nullptr;
153   }
154   if (json_key->private_key_id != nullptr) {
155     gpr_free(json_key->private_key_id);
156     json_key->private_key_id = nullptr;
157   }
158   if (json_key->client_email != nullptr) {
159     gpr_free(json_key->client_email);
160     json_key->client_email = nullptr;
161   }
162   if (json_key->private_key != nullptr) {
163 #if OPENSSL_VERSION_NUMBER < 0x30000000L
164     RSA_free(json_key->private_key);
165 #else
166     EVP_PKEY_free(json_key->private_key);
167 #endif
168     json_key->private_key = nullptr;
169   }
170 }
171 
172 // --- jwt encoding and signature. ---
173 
encoded_jwt_header(const char * key_id,const char * algorithm)174 static char* encoded_jwt_header(const char* key_id, const char* algorithm) {
175   Json json = Json::FromObject({
176       {"alg", Json::FromString(algorithm)},
177       {"typ", Json::FromString(GRPC_JWT_TYPE)},
178       {"kid", Json::FromString(key_id)},
179   });
180   std::string json_str = grpc_core::JsonDump(json);
181   return gpr_strdup(absl::WebSafeBase64Escape(json_str).c_str());
182 }
183 
encoded_jwt_claim(const grpc_auth_json_key * json_key,const char * audience,gpr_timespec token_lifetime,const char * scope)184 static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
185                                const char* audience,
186                                gpr_timespec token_lifetime, const char* scope) {
187   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
188   gpr_timespec expiration = gpr_time_add(now, token_lifetime);
189   if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) {
190     VLOG(2) << "Cropping token lifetime to maximum allowed value.";
191     expiration = gpr_time_add(now, grpc_max_auth_token_lifetime());
192   }
193 
194   Json::Object object = {
195       {"iss", Json::FromString(json_key->client_email)},
196       {"aud", Json::FromString(audience)},
197       {"iat", Json::FromNumber(now.tv_sec)},
198       {"exp", Json::FromNumber(expiration.tv_sec)},
199   };
200   if (scope != nullptr) {
201     object["scope"] = Json::FromString(scope);
202   } else {
203     // Unscoped JWTs need a sub field.
204     object["sub"] = Json::FromString(json_key->client_email);
205   }
206 
207   std::string json_str =
208       grpc_core::JsonDump(Json::FromObject(std::move(object)));
209   return gpr_strdup(absl::WebSafeBase64Escape(json_str).c_str());
210 }
211 
dot_concat_and_free_strings(char * str1,char * str2)212 static char* dot_concat_and_free_strings(char* str1, char* str2) {
213   size_t str1_len = strlen(str1);
214   size_t str2_len = strlen(str2);
215   size_t result_len = str1_len + 1 /* dot */ + str2_len;
216   char* result =
217       static_cast<char*>(gpr_malloc(result_len + 1 /* NULL terminated */));
218   char* current = result;
219   memcpy(current, str1, str1_len);
220   current += str1_len;
221   *(current++) = '.';
222   memcpy(current, str2, str2_len);
223   current += str2_len;
224   CHECK(current >= result);
225   CHECK((uintptr_t)(current - result) == result_len);
226   *current = '\0';
227   gpr_free(str1);
228   gpr_free(str2);
229   return result;
230 }
231 
openssl_digest_from_algorithm(const char * algorithm)232 const EVP_MD* openssl_digest_from_algorithm(const char* algorithm) {
233   if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) {
234     return EVP_sha256();
235   } else {
236     LOG(ERROR) << "Unknown algorithm " << algorithm;
237     return nullptr;
238   }
239 }
240 
compute_and_encode_signature(const grpc_auth_json_key * json_key,const char * signature_algorithm,const char * to_sign)241 char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
242                                    const char* signature_algorithm,
243                                    const char* to_sign) {
244   const EVP_MD* md = openssl_digest_from_algorithm(signature_algorithm);
245   EVP_MD_CTX* md_ctx = nullptr;
246 #if OPENSSL_VERSION_NUMBER < 0x30000000L
247   EVP_PKEY* key = EVP_PKEY_new();
248 #endif
249   size_t sig_len = 0;
250   unsigned char* sig = nullptr;
251   char* result = nullptr;
252   if (md == nullptr) return nullptr;
253   md_ctx = EVP_MD_CTX_create();
254   if (md_ctx == nullptr) {
255     LOG(ERROR) << "Could not create MD_CTX";
256     goto end;
257   }
258 #if OPENSSL_VERSION_NUMBER < 0x30000000L
259   EVP_PKEY_set1_RSA(key, json_key->private_key);
260   if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, key) != 1) {
261 #else
262   if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, json_key->private_key) !=
263       1) {
264 #endif
265     LOG(ERROR) << "DigestInit failed.";
266     goto end;
267   }
268   if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) {
269     LOG(ERROR) << "DigestUpdate failed.";
270     goto end;
271   }
272   if (EVP_DigestSignFinal(md_ctx, nullptr, &sig_len) != 1) {
273     LOG(ERROR) << "DigestFinal (get signature length) failed.";
274     goto end;
275   }
276   sig = static_cast<unsigned char*>(gpr_malloc(sig_len));
277   if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) {
278     LOG(ERROR) << "DigestFinal (signature compute) failed.";
279     goto end;
280   }
281   result =
282       gpr_strdup(absl::WebSafeBase64Escape(
283                      absl::string_view(reinterpret_cast<char*>(sig), sig_len))
284                      .c_str());
285 
286 end:
287 #if OPENSSL_VERSION_NUMBER < 0x30000000L
288   if (key != nullptr) EVP_PKEY_free(key);
289 #endif
290   if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx);
291   if (sig != nullptr) gpr_free(sig);
292   return result;
293 }
294 
295 char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
296                                const char* audience,
297                                gpr_timespec token_lifetime, const char* scope) {
298   if (g_jwt_encode_and_sign_override != nullptr) {
299     return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime,
300                                           scope);
301   } else {
302     const char* sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM;
303     char* to_sign = dot_concat_and_free_strings(
304         encoded_jwt_header(json_key->private_key_id, sig_algo),
305         encoded_jwt_claim(json_key, audience, token_lifetime, scope));
306     char* sig = compute_and_encode_signature(json_key, sig_algo, to_sign);
307     if (sig == nullptr) {
308       gpr_free(to_sign);
309       return nullptr;
310     }
311     return dot_concat_and_free_strings(to_sign, sig);
312   }
313 }
314 
315 void grpc_jwt_encode_and_sign_set_override(
316     grpc_jwt_encode_and_sign_override func) {
317   g_jwt_encode_and_sign_override = func;
318 }
319