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