Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(391)

Side by Side Diff: third_party/grpc/src/core/security/json_token.c

Issue 1932353002: Initial checkin of gRPC to third_party/ Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 *
3 * Copyright 2015-2016, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34 #include "src/core/security/json_token.h"
35
36 #include <string.h>
37
38 #include <grpc/support/alloc.h>
39 #include <grpc/support/log.h>
40 #include <grpc/support/string_util.h>
41
42 #include "src/core/security/b64.h"
43 #include "src/core/support/string.h"
44
45 #include <openssl/bio.h>
46 #include <openssl/evp.h>
47 #include <openssl/pem.h>
48
49 /* --- Constants. --- */
50
51 /* 1 hour max. */
52 gpr_timespec grpc_max_auth_token_lifetime() {
53 gpr_timespec out;
54 out.tv_sec = 3600;
55 out.tv_nsec = 0;
56 out.clock_type = GPR_TIMESPAN;
57 return out;
58 }
59
60 #define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256"
61 #define GRPC_JWT_TYPE "JWT"
62
63 /* --- Override for testing. --- */
64
65 static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL;
66
67 /* --- grpc_auth_json_key. --- */
68
69 static const char *json_get_string_property(const grpc_json *json,
70 const char *prop_name) {
71 grpc_json *child;
72 for (child = json->child; child != NULL; child = child->next) {
73 if (strcmp(child->key, prop_name) == 0) break;
74 }
75 if (child == NULL || child->type != GRPC_JSON_STRING) {
76 gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
77 return NULL;
78 }
79 return child->value;
80 }
81
82 static int set_json_key_string_property(const grpc_json *json,
83 const char *prop_name,
84 char **json_key_field) {
85 const char *prop_value = json_get_string_property(json, prop_name);
86 if (prop_value == NULL) return 0;
87 *json_key_field = gpr_strdup(prop_value);
88 return 1;
89 }
90
91 int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) {
92 return (json_key != NULL) &&
93 strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID);
94 }
95
96 grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json) {
97 grpc_auth_json_key result;
98 BIO *bio = NULL;
99 const char *prop_value;
100 int success = 0;
101
102 memset(&result, 0, sizeof(grpc_auth_json_key));
103 result.type = GRPC_AUTH_JSON_TYPE_INVALID;
104 if (json == NULL) {
105 gpr_log(GPR_ERROR, "Invalid json.");
106 goto end;
107 }
108
109 prop_value = json_get_string_property(json, "type");
110 if (prop_value == NULL ||
111 strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) {
112 goto end;
113 }
114 result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT;
115
116 if (!set_json_key_string_property(json, "private_key_id",
117 &result.private_key_id) ||
118 !set_json_key_string_property(json, "client_id", &result.client_id) ||
119 !set_json_key_string_property(json, "client_email",
120 &result.client_email)) {
121 goto end;
122 }
123
124 prop_value = json_get_string_property(json, "private_key");
125 if (prop_value == NULL) {
126 goto end;
127 }
128 bio = BIO_new(BIO_s_mem());
129 success = BIO_puts(bio, prop_value);
130 if ((success < 0) || ((size_t)success != strlen(prop_value))) {
131 gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
132 goto end;
133 }
134 result.private_key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, "");
135 if (result.private_key == NULL) {
136 gpr_log(GPR_ERROR, "Could not deserialize private key.");
137 goto end;
138 }
139 success = 1;
140
141 end:
142 if (bio != NULL) BIO_free(bio);
143 if (!success) grpc_auth_json_key_destruct(&result);
144 return result;
145 }
146
147 grpc_auth_json_key grpc_auth_json_key_create_from_string(
148 const char *json_string) {
149 char *scratchpad = gpr_strdup(json_string);
150 grpc_json *json = grpc_json_parse_string(scratchpad);
151 grpc_auth_json_key result = grpc_auth_json_key_create_from_json(json);
152 if (json != NULL) grpc_json_destroy(json);
153 gpr_free(scratchpad);
154 return result;
155 }
156
157 void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) {
158 if (json_key == NULL) return;
159 json_key->type = GRPC_AUTH_JSON_TYPE_INVALID;
160 if (json_key->client_id != NULL) {
161 gpr_free(json_key->client_id);
162 json_key->client_id = NULL;
163 }
164 if (json_key->private_key_id != NULL) {
165 gpr_free(json_key->private_key_id);
166 json_key->private_key_id = NULL;
167 }
168 if (json_key->client_email != NULL) {
169 gpr_free(json_key->client_email);
170 json_key->client_email = NULL;
171 }
172 if (json_key->private_key != NULL) {
173 RSA_free(json_key->private_key);
174 json_key->private_key = NULL;
175 }
176 }
177
178 /* --- jwt encoding and signature. --- */
179
180 static grpc_json *create_child(grpc_json *brother, grpc_json *parent,
181 const char *key, const char *value,
182 grpc_json_type type) {
183 grpc_json *child = grpc_json_create(type);
184 if (brother) brother->next = child;
185 if (!parent->child) parent->child = child;
186 child->parent = parent;
187 child->value = value;
188 child->key = key;
189 return child;
190 }
191
192 static char *encoded_jwt_header(const char *key_id, const char *algorithm) {
193 grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
194 grpc_json *child = NULL;
195 char *json_str = NULL;
196 char *result = NULL;
197
198 child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING);
199 child = create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING);
200 create_child(child, json, "kid", key_id, GRPC_JSON_STRING);
201
202 json_str = grpc_json_dump_to_string(json, 0);
203 result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
204 gpr_free(json_str);
205 grpc_json_destroy(json);
206 return result;
207 }
208
209 static char *encoded_jwt_claim(const grpc_auth_json_key *json_key,
210 const char *audience,
211 gpr_timespec token_lifetime, const char *scope) {
212 grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT);
213 grpc_json *child = NULL;
214 char *json_str = NULL;
215 char *result = NULL;
216 gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
217 gpr_timespec expiration = gpr_time_add(now, token_lifetime);
218 char now_str[GPR_LTOA_MIN_BUFSIZE];
219 char expiration_str[GPR_LTOA_MIN_BUFSIZE];
220 if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) {
221 gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
222 expiration = gpr_time_add(now, grpc_max_auth_token_lifetime());
223 }
224 int64_ttoa(now.tv_sec, now_str);
225 int64_ttoa(expiration.tv_sec, expiration_str);
226
227 child =
228 create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING);
229 if (scope != NULL) {
230 child = create_child(child, json, "scope", scope, GRPC_JSON_STRING);
231 } else {
232 /* Unscoped JWTs need a sub field. */
233 child = create_child(child, json, "sub", json_key->client_email,
234 GRPC_JSON_STRING);
235 }
236
237 child = create_child(child, json, "aud", audience, GRPC_JSON_STRING);
238 child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER);
239 create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER);
240
241 json_str = grpc_json_dump_to_string(json, 0);
242 result = grpc_base64_encode(json_str, strlen(json_str), 1, 0);
243 gpr_free(json_str);
244 grpc_json_destroy(json);
245 return result;
246 }
247
248 static char *dot_concat_and_free_strings(char *str1, char *str2) {
249 size_t str1_len = strlen(str1);
250 size_t str2_len = strlen(str2);
251 size_t result_len = str1_len + 1 /* dot */ + str2_len;
252 char *result = gpr_malloc(result_len + 1 /* NULL terminated */);
253 char *current = result;
254 memcpy(current, str1, str1_len);
255 current += str1_len;
256 *(current++) = '.';
257 memcpy(current, str2, str2_len);
258 current += str2_len;
259 GPR_ASSERT(current >= result);
260 GPR_ASSERT((uintptr_t)(current - result) == result_len);
261 *current = '\0';
262 gpr_free(str1);
263 gpr_free(str2);
264 return result;
265 }
266
267 const EVP_MD *openssl_digest_from_algorithm(const char *algorithm) {
268 if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) {
269 return EVP_sha256();
270 } else {
271 gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm);
272 return NULL;
273 }
274 }
275
276 char *compute_and_encode_signature(const grpc_auth_json_key *json_key,
277 const char *signature_algorithm,
278 const char *to_sign) {
279 const EVP_MD *md = openssl_digest_from_algorithm(signature_algorithm);
280 EVP_MD_CTX *md_ctx = NULL;
281 EVP_PKEY *key = EVP_PKEY_new();
282 size_t sig_len = 0;
283 unsigned char *sig = NULL;
284 char *result = NULL;
285 if (md == NULL) return NULL;
286 md_ctx = EVP_MD_CTX_create();
287 if (md_ctx == NULL) {
288 gpr_log(GPR_ERROR, "Could not create MD_CTX");
289 goto end;
290 }
291 EVP_PKEY_set1_RSA(key, json_key->private_key);
292 if (EVP_DigestSignInit(md_ctx, NULL, md, NULL, key) != 1) {
293 gpr_log(GPR_ERROR, "DigestInit failed.");
294 goto end;
295 }
296 if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) {
297 gpr_log(GPR_ERROR, "DigestUpdate failed.");
298 goto end;
299 }
300 if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) != 1) {
301 gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed.");
302 goto end;
303 }
304 sig = gpr_malloc(sig_len);
305 if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) {
306 gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed.");
307 goto end;
308 }
309 result = grpc_base64_encode(sig, sig_len, 1, 0);
310
311 end:
312 if (key != NULL) EVP_PKEY_free(key);
313 if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx);
314 if (sig != NULL) gpr_free(sig);
315 return result;
316 }
317
318 char *grpc_jwt_encode_and_sign(const grpc_auth_json_key *json_key,
319 const char *audience,
320 gpr_timespec token_lifetime, const char *scope) {
321 if (g_jwt_encode_and_sign_override != NULL) {
322 return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime,
323 scope);
324 } else {
325 const char *sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM;
326 char *to_sign = dot_concat_and_free_strings(
327 encoded_jwt_header(json_key->private_key_id, sig_algo),
328 encoded_jwt_claim(json_key, audience, token_lifetime, scope));
329 char *sig = compute_and_encode_signature(json_key, sig_algo, to_sign);
330 if (sig == NULL) {
331 gpr_free(to_sign);
332 return NULL;
333 }
334 return dot_concat_and_free_strings(to_sign, sig);
335 }
336 }
337
338 void grpc_jwt_encode_and_sign_set_override(
339 grpc_jwt_encode_and_sign_override func) {
340 g_jwt_encode_and_sign_override = func;
341 }
342
343 /* --- grpc_auth_refresh_token --- */
344
345 int grpc_auth_refresh_token_is_valid(
346 const grpc_auth_refresh_token *refresh_token) {
347 return (refresh_token != NULL) &&
348 strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
349 }
350
351 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
352 const grpc_json *json) {
353 grpc_auth_refresh_token result;
354 const char *prop_value;
355 int success = 0;
356
357 memset(&result, 0, sizeof(grpc_auth_refresh_token));
358 result.type = GRPC_AUTH_JSON_TYPE_INVALID;
359 if (json == NULL) {
360 gpr_log(GPR_ERROR, "Invalid json.");
361 goto end;
362 }
363
364 prop_value = json_get_string_property(json, "type");
365 if (prop_value == NULL ||
366 strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
367 goto end;
368 }
369 result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
370
371 if (!set_json_key_string_property(json, "client_secret",
372 &result.client_secret) ||
373 !set_json_key_string_property(json, "client_id", &result.client_id) ||
374 !set_json_key_string_property(json, "refresh_token",
375 &result.refresh_token)) {
376 goto end;
377 }
378 success = 1;
379
380 end:
381 if (!success) grpc_auth_refresh_token_destruct(&result);
382 return result;
383 }
384
385 grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
386 const char *json_string) {
387 char *scratchpad = gpr_strdup(json_string);
388 grpc_json *json = grpc_json_parse_string(scratchpad);
389 grpc_auth_refresh_token result =
390 grpc_auth_refresh_token_create_from_json(json);
391 if (json != NULL) grpc_json_destroy(json);
392 gpr_free(scratchpad);
393 return result;
394 }
395
396 void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) {
397 if (refresh_token == NULL) return;
398 refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
399 if (refresh_token->client_id != NULL) {
400 gpr_free(refresh_token->client_id);
401 refresh_token->client_id = NULL;
402 }
403 if (refresh_token->client_secret != NULL) {
404 gpr_free(refresh_token->client_secret);
405 refresh_token->client_secret = NULL;
406 }
407 if (refresh_token->refresh_token != NULL) {
408 gpr_free(refresh_token->refresh_token);
409 refresh_token->refresh_token = NULL;
410 }
411 }
OLDNEW
« no previous file with comments | « third_party/grpc/src/core/security/json_token.h ('k') | third_party/grpc/src/core/security/jwt_verifier.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698