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