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