• 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 <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