Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc / deps / grpc / src / core / lib / security / credentials / jwt / jwt_verifier.cc
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/tsi/grpc_shadow_boringssl.h"
22
23 #include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
24
25 #include <limits.h>
26 #include <string.h>
27
28 #include <grpc/support/alloc.h>
29 #include <grpc/support/log.h>
30 #include <grpc/support/string_util.h>
31 #include <grpc/support/sync.h>
32
33 extern "C" {
34 #include <openssl/bn.h>
35 #include <openssl/pem.h>
36 #include <openssl/rsa.h>
37 }
38
39 #include "src/core/lib/gpr/string.h"
40 #include "src/core/lib/http/httpcli.h"
41 #include "src/core/lib/iomgr/polling_entity.h"
42 #include "src/core/lib/slice/b64.h"
43 #include "src/core/lib/slice/slice_internal.h"
44 #include "src/core/tsi/ssl_types.h"
45
46 /* --- Utils. --- */
47
48 const char* grpc_jwt_verifier_status_to_string(
49     grpc_jwt_verifier_status status) {
50   switch (status) {
51     case GRPC_JWT_VERIFIER_OK:
52       return "OK";
53     case GRPC_JWT_VERIFIER_BAD_SIGNATURE:
54       return "BAD_SIGNATURE";
55     case GRPC_JWT_VERIFIER_BAD_FORMAT:
56       return "BAD_FORMAT";
57     case GRPC_JWT_VERIFIER_BAD_AUDIENCE:
58       return "BAD_AUDIENCE";
59     case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR:
60       return "KEY_RETRIEVAL_ERROR";
61     case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE:
62       return "TIME_CONSTRAINT_FAILURE";
63     case GRPC_JWT_VERIFIER_GENERIC_ERROR:
64       return "GENERIC_ERROR";
65     default:
66       return "UNKNOWN";
67   }
68 }
69
70 static const EVP_MD* evp_md_from_alg(const char* alg) {
71   if (strcmp(alg, "RS256") == 0) {
72     return EVP_sha256();
73   } else if (strcmp(alg, "RS384") == 0) {
74     return EVP_sha384();
75   } else if (strcmp(alg, "RS512") == 0) {
76     return EVP_sha512();
77   } else {
78     return nullptr;
79   }
80 }
81
82 static grpc_json* parse_json_part_from_jwt(const char* str, size_t len,
83                                            grpc_slice* buffer) {
84   grpc_json* json;
85
86   *buffer = grpc_base64_decode_with_len(str, len, 1);
87   if (GRPC_SLICE_IS_EMPTY(*buffer)) {
88     gpr_log(GPR_ERROR, "Invalid base64.");
89     return nullptr;
90   }
91   json = grpc_json_parse_string_with_len(
92       reinterpret_cast<char*> GRPC_SLICE_START_PTR(*buffer),
93       GRPC_SLICE_LENGTH(*buffer));
94   if (json == nullptr) {
95     grpc_slice_unref_internal(*buffer);
96     gpr_log(GPR_ERROR, "JSON parsing error.");
97   }
98   return json;
99 }
100
101 static const char* validate_string_field(const grpc_json* json,
102                                          const char* key) {
103   if (json->type != GRPC_JSON_STRING) {
104     gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
105     return nullptr;
106   }
107   return json->value;
108 }
109
110 static gpr_timespec validate_time_field(const grpc_json* json,
111                                         const char* key) {
112   gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME);
113   if (json->type != GRPC_JSON_NUMBER) {
114     gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
115     return result;
116   }
117   result.tv_sec = strtol(json->value, nullptr, 10);
118   return result;
119 }
120
121 /* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */
122
123 typedef struct {
124   const char* alg;
125   const char* kid;
126   const char* typ;
127   /* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */
128   grpc_slice buffer;
129 } jose_header;
130
131 static void jose_header_destroy(jose_header* h) {
132   grpc_slice_unref_internal(h->buffer);
133   gpr_free(h);
134 }
135
136 /* Takes ownership of json and buffer. */
137 static jose_header* jose_header_from_json(grpc_json* json,
138                                           const grpc_slice& buffer) {
139   grpc_json* cur;
140   jose_header* h = static_cast<jose_header*>(gpr_zalloc(sizeof(jose_header)));
141   h->buffer = buffer;
142   for (cur = json->child; cur != nullptr; cur = cur->next) {
143     if (strcmp(cur->key, "alg") == 0) {
144       /* We only support RSA-1.5 signatures for now.
145          Beware of this if we add HMAC support:
146          https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
147        */
148       if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) ||
149           evp_md_from_alg(cur->value) == nullptr) {
150         gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value);
151         goto error;
152       }
153       h->alg = cur->value;
154     } else if (strcmp(cur->key, "typ") == 0) {
155       h->typ = validate_string_field(cur, "typ");
156       if (h->typ == nullptr) goto error;
157     } else if (strcmp(cur->key, "kid") == 0) {
158       h->kid = validate_string_field(cur, "kid");
159       if (h->kid == nullptr) goto error;
160     }
161   }
162   if (h->alg == nullptr) {
163     gpr_log(GPR_ERROR, "Missing alg field.");
164     goto error;
165   }
166   grpc_json_destroy(json);
167   h->buffer = buffer;
168   return h;
169
170 error:
171   grpc_json_destroy(json);
172   jose_header_destroy(h);
173   return nullptr;
174 }
175
176 /* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */
177
178 struct grpc_jwt_claims {
179   /* Well known properties already parsed. */
180   const char* sub;
181   const char* iss;
182   const char* aud;
183   const char* jti;
184   gpr_timespec iat;
185   gpr_timespec exp;
186   gpr_timespec nbf;
187
188   grpc_json* json;
189   grpc_slice buffer;
190 };
191
192 void grpc_jwt_claims_destroy(grpc_jwt_claims* claims) {
193   grpc_json_destroy(claims->json);
194   grpc_slice_unref_internal(claims->buffer);
195   gpr_free(claims);
196 }
197
198 const grpc_json* grpc_jwt_claims_json(const grpc_jwt_claims* claims) {
199   if (claims == nullptr) return nullptr;
200   return claims->json;
201 }
202
203 const char* grpc_jwt_claims_subject(const grpc_jwt_claims* claims) {
204   if (claims == nullptr) return nullptr;
205   return claims->sub;
206 }
207
208 const char* grpc_jwt_claims_issuer(const grpc_jwt_claims* claims) {
209   if (claims == nullptr) return nullptr;
210   return claims->iss;
211 }
212
213 const char* grpc_jwt_claims_id(const grpc_jwt_claims* claims) {
214   if (claims == nullptr) return nullptr;
215   return claims->jti;
216 }
217
218 const char* grpc_jwt_claims_audience(const grpc_jwt_claims* claims) {
219   if (claims == nullptr) return nullptr;
220   return claims->aud;
221 }
222
223 gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims* claims) {
224   if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME);
225   return claims->iat;
226 }
227
228 gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims* claims) {
229   if (claims == nullptr) return gpr_inf_future(GPR_CLOCK_REALTIME);
230   return claims->exp;
231 }
232
233 gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims* claims) {
234   if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME);
235   return claims->nbf;
236 }
237
238 /* Takes ownership of json and buffer even in case of failure. */
239 grpc_jwt_claims* grpc_jwt_claims_from_json(grpc_json* json,
240                                            const grpc_slice& buffer) {
241   grpc_json* cur;
242   grpc_jwt_claims* claims =
243       static_cast<grpc_jwt_claims*>(gpr_malloc(sizeof(grpc_jwt_claims)));
244   memset(claims, 0, sizeof(grpc_jwt_claims));
245   claims->json = json;
246   claims->buffer = buffer;
247   claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME);
248   claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME);
249   claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME);
250
251   /* Per the spec, all fields are optional. */
252   for (cur = json->child; cur != nullptr; cur = cur->next) {
253     if (strcmp(cur->key, "sub") == 0) {
254       claims->sub = validate_string_field(cur, "sub");
255       if (claims->sub == nullptr) goto error;
256     } else if (strcmp(cur->key, "iss") == 0) {
257       claims->iss = validate_string_field(cur, "iss");
258       if (claims->iss == nullptr) goto error;
259     } else if (strcmp(cur->key, "aud") == 0) {
260       claims->aud = validate_string_field(cur, "aud");
261       if (claims->aud == nullptr) goto error;
262     } else if (strcmp(cur->key, "jti") == 0) {
263       claims->jti = validate_string_field(cur, "jti");
264       if (claims->jti == nullptr) goto error;
265     } else if (strcmp(cur->key, "iat") == 0) {
266       claims->iat = validate_time_field(cur, "iat");
267       if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
268         goto error;
269     } else if (strcmp(cur->key, "exp") == 0) {
270       claims->exp = validate_time_field(cur, "exp");
271       if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
272         goto error;
273     } else if (strcmp(cur->key, "nbf") == 0) {
274       claims->nbf = validate_time_field(cur, "nbf");
275       if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
276         goto error;
277     }
278   }
279   return claims;
280
281 error:
282   grpc_jwt_claims_destroy(claims);
283   return nullptr;
284 }
285
286 grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims* claims,
287                                                const char* audience) {
288   gpr_timespec skewed_now;
289   int audience_ok;
290
291   GPR_ASSERT(claims != nullptr);
292
293   skewed_now =
294       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew);
295   if (gpr_time_cmp(skewed_now, claims->nbf) < 0) {
296     gpr_log(GPR_ERROR, "JWT is not valid yet.");
297     return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
298   }
299   skewed_now =
300       gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew);
301   if (gpr_time_cmp(skewed_now, claims->exp) > 0) {
302     gpr_log(GPR_ERROR, "JWT is expired.");
303     return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
304   }
305
306   /* This should be probably up to the upper layer to decide but let's harcode
307      the 99% use case here for email issuers, where the JWT must be self
308      issued. */
309   if (grpc_jwt_issuer_email_domain(claims->iss) != nullptr &&
310       claims->sub != nullptr && strcmp(claims->iss, claims->sub) != 0) {
311     gpr_log(GPR_ERROR,
312             "Email issuer (%s) cannot assert another subject (%s) than itself.",
313             claims->iss, claims->sub);
314     return GRPC_JWT_VERIFIER_BAD_SUBJECT;
315   }
316
317   if (audience == nullptr) {
318     audience_ok = claims->aud == nullptr;
319   } else {
320     audience_ok = claims->aud != nullptr && strcmp(audience, claims->aud) == 0;
321   }
322   if (!audience_ok) {
323     gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.",
324             audience == nullptr ? "NULL" : audience,
325             claims->aud == nullptr ? "NULL" : claims->aud);
326     return GRPC_JWT_VERIFIER_BAD_AUDIENCE;
327   }
328   return GRPC_JWT_VERIFIER_OK;
329 }
330
331 /* --- verifier_cb_ctx object. --- */
332
333 typedef enum {
334   HTTP_RESPONSE_OPENID = 0,
335   HTTP_RESPONSE_KEYS,
336   HTTP_RESPONSE_COUNT /* must be last */
337 } http_response_index;
338
339 typedef struct {
340   grpc_jwt_verifier* verifier;
341   grpc_polling_entity pollent;
342   jose_header* header;
343   grpc_jwt_claims* claims;
344   char* audience;
345   grpc_slice signature;
346   grpc_slice signed_data;
347   void* user_data;
348   grpc_jwt_verification_done_cb user_cb;
349   grpc_http_response responses[HTTP_RESPONSE_COUNT];
350 } verifier_cb_ctx;
351
352 /* Takes ownership of the header, claims and signature. */
353 static verifier_cb_ctx* verifier_cb_ctx_create(
354     grpc_jwt_verifier* verifier, grpc_pollset* pollset, jose_header* header,
355     grpc_jwt_claims* claims, const char* audience, const grpc_slice& signature,
356     const char* signed_jwt, size_t signed_jwt_len, void* user_data,
357     grpc_jwt_verification_done_cb cb) {
358   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
359   grpc_core::ExecCtx exec_ctx;
360   verifier_cb_ctx* ctx =
361       static_cast<verifier_cb_ctx*>(gpr_zalloc(sizeof(verifier_cb_ctx)));
362   ctx->verifier = verifier;
363   ctx->pollent = grpc_polling_entity_create_from_pollset(pollset);
364   ctx->header = header;
365   ctx->audience = gpr_strdup(audience);
366   ctx->claims = claims;
367   ctx->signature = signature;
368   ctx->signed_data = grpc_slice_from_copied_buffer(signed_jwt, signed_jwt_len);
369   ctx->user_data = user_data;
370   ctx->user_cb = cb;
371
372   return ctx;
373 }
374
375 void verifier_cb_ctx_destroy(verifier_cb_ctx* ctx) {
376   if (ctx->audience != nullptr) gpr_free(ctx->audience);
377   if (ctx->claims != nullptr) grpc_jwt_claims_destroy(ctx->claims);
378   grpc_slice_unref_internal(ctx->signature);
379   grpc_slice_unref_internal(ctx->signed_data);
380   jose_header_destroy(ctx->header);
381   for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) {
382     grpc_http_response_destroy(&ctx->responses[i]);
383   }
384   /* TODO: see what to do with claims... */
385   gpr_free(ctx);
386 }
387
388 /* --- grpc_jwt_verifier object. --- */
389
390 /* Clock skew defaults to one minute. */
391 gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN};
392
393 /* Max delay defaults to one minute. */
394 grpc_millis grpc_jwt_verifier_max_delay = 60 * GPR_MS_PER_SEC;
395
396 typedef struct {
397   char* email_domain;
398   char* key_url_prefix;
399 } email_key_mapping;
400
401 struct grpc_jwt_verifier {
402   email_key_mapping* mappings;
403   size_t num_mappings; /* Should be very few, linear search ok. */
404   size_t allocated_mappings;
405   grpc_httpcli_context http_ctx;
406 };
407
408 static grpc_json* json_from_http(const grpc_httpcli_response* response) {
409   grpc_json* json = nullptr;
410
411   if (response == nullptr) {
412     gpr_log(GPR_ERROR, "HTTP response is NULL.");
413     return nullptr;
414   }
415   if (response->status != 200) {
416     gpr_log(GPR_ERROR, "Call to http server failed with error %d.",
417             response->status);
418     return nullptr;
419   }
420
421   json = grpc_json_parse_string_with_len(response->body, response->body_length);
422   if (json == nullptr) {
423     gpr_log(GPR_ERROR, "Invalid JSON found in response.");
424   }
425   return json;
426 }
427
428 static const grpc_json* find_property_by_name(const grpc_json* json,
429                                               const char* name) {
430   const grpc_json* cur;
431   for (cur = json->child; cur != nullptr; cur = cur->next) {
432     if (strcmp(cur->key, name) == 0) return cur;
433   }
434   return nullptr;
435 }
436
437 static EVP_PKEY* extract_pkey_from_x509(const char* x509_str) {
438   X509* x509 = nullptr;
439   EVP_PKEY* result = nullptr;
440   BIO* bio = BIO_new(BIO_s_mem());
441   size_t len = strlen(x509_str);
442   GPR_ASSERT(len < INT_MAX);
443   BIO_write(bio, x509_str, static_cast<int>(len));
444   x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
445   if (x509 == nullptr) {
446     gpr_log(GPR_ERROR, "Unable to parse x509 cert.");
447     goto end;
448   }
449   result = X509_get_pubkey(x509);
450   if (result == nullptr) {
451     gpr_log(GPR_ERROR, "Cannot find public key in X509 cert.");
452   }
453
454 end:
455   BIO_free(bio);
456   X509_free(x509);
457   return result;
458 }
459
460 static BIGNUM* bignum_from_base64(const char* b64) {
461   BIGNUM* result = nullptr;
462   grpc_slice bin;
463
464   if (b64 == nullptr) return nullptr;
465   bin = grpc_base64_decode(b64, 1);
466   if (GRPC_SLICE_IS_EMPTY(bin)) {
467     gpr_log(GPR_ERROR, "Invalid base64 for big num.");
468     return nullptr;
469   }
470   result = BN_bin2bn(GRPC_SLICE_START_PTR(bin),
471                      TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), nullptr);
472   grpc_slice_unref_internal(bin);
473   return result;
474 }
475
476 #if OPENSSL_VERSION_NUMBER < 0x10100000L
477
478 // Provide compatibility across OpenSSL 1.02 and 1.1.
479 static int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) {
480   /* If the fields n and e in r are NULL, the corresponding input
481    * parameters MUST be non-NULL for n and e.  d may be
482    * left NULL (in case only the public key is used).
483    */
484   if ((r->n == nullptr && n == nullptr) || (r->e == nullptr && e == nullptr)) {
485     return 0;
486   }
487
488   if (n != nullptr) {
489     BN_free(r->n);
490     r->n = n;
491   }
492   if (e != nullptr) {
493     BN_free(r->e);
494     r->e = e;
495   }
496   if (d != nullptr) {
497     BN_free(r->d);
498     r->d = d;
499   }
500
501   return 1;
502 }
503 #endif  // OPENSSL_VERSION_NUMBER < 0x10100000L
504
505 static EVP_PKEY* pkey_from_jwk(const grpc_json* json, const char* kty) {
506   const grpc_json* key_prop;
507   RSA* rsa = nullptr;
508   EVP_PKEY* result = nullptr;
509   BIGNUM* tmp_n = nullptr;
510   BIGNUM* tmp_e = nullptr;
511
512   GPR_ASSERT(kty != nullptr && json != nullptr);
513   if (strcmp(kty, "RSA") != 0) {
514     gpr_log(GPR_ERROR, "Unsupported key type %s.", kty);
515     goto end;
516   }
517   rsa = RSA_new();
518   if (rsa == nullptr) {
519     gpr_log(GPR_ERROR, "Could not create rsa key.");
520     goto end;
521   }
522   for (key_prop = json->child; key_prop != nullptr; key_prop = key_prop->next) {
523     if (strcmp(key_prop->key, "n") == 0) {
524       tmp_n = bignum_from_base64(validate_string_field(key_prop, "n"));
525       if (tmp_n == nullptr) goto end;
526     } else if (strcmp(key_prop->key, "e") == 0) {
527       tmp_e = bignum_from_base64(validate_string_field(key_prop, "e"));
528       if (tmp_e == nullptr) goto end;
529     }
530   }
531   if (tmp_e == nullptr || tmp_n == nullptr) {
532     gpr_log(GPR_ERROR, "Missing RSA public key field.");
533     goto end;
534   }
535   if (!RSA_set0_key(rsa, tmp_n, tmp_e, nullptr)) {
536     gpr_log(GPR_ERROR, "Cannot set RSA key from inputs.");
537     goto end;
538   }
539   /* RSA_set0_key takes ownership on success. */
540   tmp_n = nullptr;
541   tmp_e = nullptr;
542   result = EVP_PKEY_new();
543   EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */
544
545 end:
546   RSA_free(rsa);
547   BN_free(tmp_n);
548   BN_free(tmp_e);
549   return result;
550 }
551
552 static EVP_PKEY* find_verification_key(const grpc_json* json,
553                                        const char* header_alg,
554                                        const char* header_kid) {
555   const grpc_json* jkey;
556   const grpc_json* jwk_keys;
557   /* Try to parse the json as a JWK set:
558      https://tools.ietf.org/html/rfc7517#section-5. */
559   jwk_keys = find_property_by_name(json, "keys");
560   if (jwk_keys == nullptr) {
561     /* Use the google proprietary format which is:
562        { <kid1>: <x5091>, <kid2>: <x5092>, ... } */
563     const grpc_json* cur = find_property_by_name(json, header_kid);
564     if (cur == nullptr) return nullptr;
565     return extract_pkey_from_x509(cur->value);
566   }
567
568   if (jwk_keys->type != GRPC_JSON_ARRAY) {
569     gpr_log(GPR_ERROR,
570             "Unexpected value type of keys property in jwks key set.");
571     return nullptr;
572   }
573   /* Key format is specified in:
574      https://tools.ietf.org/html/rfc7518#section-6. */
575   for (jkey = jwk_keys->child; jkey != nullptr; jkey = jkey->next) {
576     grpc_json* key_prop;
577     const char* alg = nullptr;
578     const char* kid = nullptr;
579     const char* kty = nullptr;
580
581     if (jkey->type != GRPC_JSON_OBJECT) continue;
582     for (key_prop = jkey->child; key_prop != nullptr;
583          key_prop = key_prop->next) {
584       if (strcmp(key_prop->key, "alg") == 0 &&
585           key_prop->type == GRPC_JSON_STRING) {
586         alg = key_prop->value;
587       } else if (strcmp(key_prop->key, "kid") == 0 &&
588                  key_prop->type == GRPC_JSON_STRING) {
589         kid = key_prop->value;
590       } else if (strcmp(key_prop->key, "kty") == 0 &&
591                  key_prop->type == GRPC_JSON_STRING) {
592         kty = key_prop->value;
593       }
594     }
595     if (alg != nullptr && kid != nullptr && kty != nullptr &&
596         strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) {
597       return pkey_from_jwk(jkey, kty);
598     }
599   }
600   gpr_log(GPR_ERROR,
601           "Could not find matching key in key set for kid=%s and alg=%s",
602           header_kid, header_alg);
603   return nullptr;
604 }
605
606 static int verify_jwt_signature(EVP_PKEY* key, const char* alg,
607                                 const grpc_slice& signature,
608                                 const grpc_slice& signed_data) {
609   EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
610   const EVP_MD* md = evp_md_from_alg(alg);
611   int result = 0;
612
613   GPR_ASSERT(md != nullptr); /* Checked before. */
614   if (md_ctx == nullptr) {
615     gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX.");
616     goto end;
617   }
618   if (EVP_DigestVerifyInit(md_ctx, nullptr, md, nullptr, key) != 1) {
619     gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed.");
620     goto end;
621   }
622   if (EVP_DigestVerifyUpdate(md_ctx, GRPC_SLICE_START_PTR(signed_data),
623                              GRPC_SLICE_LENGTH(signed_data)) != 1) {
624     gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed.");
625     goto end;
626   }
627   if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature),
628                             GRPC_SLICE_LENGTH(signature)) != 1) {
629     gpr_log(GPR_ERROR, "JWT signature verification failed.");
630     goto end;
631   }
632   result = 1;
633
634 end:
635   EVP_MD_CTX_destroy(md_ctx);
636   return result;
637 }
638
639 static void on_keys_retrieved(void* user_data, grpc_error* error) {
640   verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
641   grpc_json* json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]);
642   EVP_PKEY* verification_key = nullptr;
643   grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
644   grpc_jwt_claims* claims = nullptr;
645
646   if (json == nullptr) {
647     status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
648     goto end;
649   }
650   verification_key =
651       find_verification_key(json, ctx->header->alg, ctx->header->kid);
652   if (verification_key == nullptr) {
653     gpr_log(GPR_ERROR, "Could not find verification key with kid %s.",
654             ctx->header->kid);
655     status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
656     goto end;
657   }
658
659   if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature,
660                             ctx->signed_data)) {
661     status = GRPC_JWT_VERIFIER_BAD_SIGNATURE;
662     goto end;
663   }
664
665   status = grpc_jwt_claims_check(ctx->claims, ctx->audience);
666   if (status == GRPC_JWT_VERIFIER_OK) {
667     /* Pass ownership. */
668     claims = ctx->claims;
669     ctx->claims = nullptr;
670   }
671
672 end:
673   grpc_json_destroy(json);
674   EVP_PKEY_free(verification_key);
675   ctx->user_cb(ctx->user_data, status, claims);
676   verifier_cb_ctx_destroy(ctx);
677 }
678
679 static void on_openid_config_retrieved(void* user_data, grpc_error* error) {
680   const grpc_json* cur;
681   verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
682   const grpc_http_response* response = &ctx->responses[HTTP_RESPONSE_OPENID];
683   grpc_json* json = json_from_http(response);
684   grpc_httpcli_request req;
685   const char* jwks_uri;
686   grpc_resource_quota* resource_quota = nullptr;
687
688   /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */
689   if (json == nullptr) goto error;
690   cur = find_property_by_name(json, "jwks_uri");
691   if (cur == nullptr) {
692     gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
693     goto error;
694   }
695   jwks_uri = validate_string_field(cur, "jwks_uri");
696   if (jwks_uri == nullptr) goto error;
697   if (strstr(jwks_uri, "https://") != jwks_uri) {
698     gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
699     goto error;
700   }
701   jwks_uri += 8;
702   req.handshaker = &grpc_httpcli_ssl;
703   req.host = gpr_strdup(jwks_uri);
704   req.http.path = const_cast<char*>(strchr(jwks_uri, '/'));
705   if (req.http.path == nullptr) {
706     req.http.path = (char*)"";
707   } else {
708     *(req.host + (req.http.path - jwks_uri)) = '\0';
709   }
710
711   /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
712      channel. This would allow us to cancel an authentication query when under
713      extreme memory pressure. */
714   resource_quota = grpc_resource_quota_create("jwt_verifier");
715   grpc_httpcli_get(
716       &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
717       grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay,
718       GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx),
719       &ctx->responses[HTTP_RESPONSE_KEYS]);
720   grpc_resource_quota_unref_internal(resource_quota);
721   grpc_json_destroy(json);
722   gpr_free(req.host);
723   return;
724
725 error:
726   grpc_json_destroy(json);
727   ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr);
728   verifier_cb_ctx_destroy(ctx);
729 }
730
731 static email_key_mapping* verifier_get_mapping(grpc_jwt_verifier* v,
732                                                const char* email_domain) {
733   size_t i;
734   if (v->mappings == nullptr) return nullptr;
735   for (i = 0; i < v->num_mappings; i++) {
736     if (strcmp(email_domain, v->mappings[i].email_domain) == 0) {
737       return &v->mappings[i];
738     }
739   }
740   return nullptr;
741 }
742
743 static void verifier_put_mapping(grpc_jwt_verifier* v, const char* email_domain,
744                                  const char* key_url_prefix) {
745   email_key_mapping* mapping = verifier_get_mapping(v, email_domain);
746   GPR_ASSERT(v->num_mappings < v->allocated_mappings);
747   if (mapping != nullptr) {
748     gpr_free(mapping->key_url_prefix);
749     mapping->key_url_prefix = gpr_strdup(key_url_prefix);
750     return;
751   }
752   v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain);
753   v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix);
754   v->num_mappings++;
755   GPR_ASSERT(v->num_mappings <= v->allocated_mappings);
756 }
757
758 /* Very non-sophisticated way to detect an email address. Should be good
759    enough for now... */
760 const char* grpc_jwt_issuer_email_domain(const char* issuer) {
761   const char* at_sign = strchr(issuer, '@');
762   if (at_sign == nullptr) return nullptr;
763   const char* email_domain = at_sign + 1;
764   if (*email_domain == '\0') return nullptr;
765   const char* dot = strrchr(email_domain, '.');
766   if (dot == nullptr || dot == email_domain) return email_domain;
767   GPR_ASSERT(dot > email_domain);
768   /* There may be a subdomain, we just want the domain. */
769   dot = static_cast<const char*>(gpr_memrchr(
770       (void*)email_domain, '.', static_cast<size_t>(dot - email_domain)));
771   if (dot == nullptr) return email_domain;
772   return dot + 1;
773 }
774
775 /* Takes ownership of ctx. */
776 static void retrieve_key_and_verify(verifier_cb_ctx* ctx) {
777   const char* email_domain;
778   grpc_closure* http_cb;
779   char* path_prefix = nullptr;
780   const char* iss;
781   grpc_httpcli_request req;
782   grpc_resource_quota* resource_quota = nullptr;
783   memset(&req, 0, sizeof(grpc_httpcli_request));
784   req.handshaker = &grpc_httpcli_ssl;
785   http_response_index rsp_idx;
786
787   GPR_ASSERT(ctx != nullptr && ctx->header != nullptr &&
788              ctx->claims != nullptr);
789   iss = ctx->claims->iss;
790   if (ctx->header->kid == nullptr) {
791     gpr_log(GPR_ERROR, "Missing kid in jose header.");
792     goto error;
793   }
794   if (iss == nullptr) {
795     gpr_log(GPR_ERROR, "Missing iss in claims.");
796     goto error;
797   }
798
799   /* This code relies on:
800      https://openid.net/specs/openid-connect-discovery-1_0.html
801      Nobody seems to implement the account/email/webfinger part 2. of the spec
802      so we will rely instead on email/url mappings if we detect such an issuer.
803      Part 4, on the other hand is implemented by both google and salesforce. */
804   email_domain = grpc_jwt_issuer_email_domain(iss);
805   if (email_domain != nullptr) {
806     email_key_mapping* mapping;
807     GPR_ASSERT(ctx->verifier != nullptr);
808     mapping = verifier_get_mapping(ctx->verifier, email_domain);
809     if (mapping == nullptr) {
810       gpr_log(GPR_ERROR, "Missing mapping for issuer email.");
811       goto error;
812     }
813     req.host = gpr_strdup(mapping->key_url_prefix);
814     path_prefix = strchr(req.host, '/');
815     if (path_prefix == nullptr) {
816       gpr_asprintf(&req.http.path, "/%s", iss);
817     } else {
818       *(path_prefix++) = '\0';
819       gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
820     }
821     http_cb =
822         GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx);
823     rsp_idx = HTTP_RESPONSE_KEYS;
824   } else {
825     req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
826     path_prefix = strchr(req.host, '/');
827     if (path_prefix == nullptr) {
828       req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
829     } else {
830       *(path_prefix++) = 0;
831       gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
832                    GRPC_OPENID_CONFIG_URL_SUFFIX);
833     }
834     http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx,
835                                   grpc_schedule_on_exec_ctx);
836     rsp_idx = HTTP_RESPONSE_OPENID;
837   }
838
839   /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
840      channel. This would allow us to cancel an authentication query when under
841      extreme memory pressure. */
842   resource_quota = grpc_resource_quota_create("jwt_verifier");
843   grpc_httpcli_get(
844       &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
845       grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay, http_cb,
846       &ctx->responses[rsp_idx]);
847   grpc_resource_quota_unref_internal(resource_quota);
848   gpr_free(req.host);
849   gpr_free(req.http.path);
850   return;
851
852 error:
853   ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr);
854   verifier_cb_ctx_destroy(ctx);
855 }
856
857 void grpc_jwt_verifier_verify(grpc_jwt_verifier* verifier,
858                               grpc_pollset* pollset, const char* jwt,
859                               const char* audience,
860                               grpc_jwt_verification_done_cb cb,
861                               void* user_data) {
862   const char* dot = nullptr;
863   grpc_json* json;
864   jose_header* header = nullptr;
865   grpc_jwt_claims* claims = nullptr;
866   grpc_slice header_buffer;
867   grpc_slice claims_buffer;
868   grpc_slice signature;
869   size_t signed_jwt_len;
870   const char* cur = jwt;
871
872   GPR_ASSERT(verifier != nullptr && jwt != nullptr && audience != nullptr &&
873              cb != nullptr);
874   dot = strchr(cur, '.');
875   if (dot == nullptr) goto error;
876   json = parse_json_part_from_jwt(cur, static_cast<size_t>(dot - cur),
877                                   &header_buffer);
878   if (json == nullptr) goto error;
879   header = jose_header_from_json(json, header_buffer);
880   if (header == nullptr) goto error;
881
882   cur = dot + 1;
883   dot = strchr(cur, '.');
884   if (dot == nullptr) goto error;
885   json = parse_json_part_from_jwt(cur, static_cast<size_t>(dot - cur),
886                                   &claims_buffer);
887   if (json == nullptr) goto error;
888   claims = grpc_jwt_claims_from_json(json, claims_buffer);
889   if (claims == nullptr) goto error;
890
891   signed_jwt_len = static_cast<size_t>(dot - jwt);
892   cur = dot + 1;
893   signature = grpc_base64_decode(cur, 1);
894   if (GRPC_SLICE_IS_EMPTY(signature)) goto error;
895   retrieve_key_and_verify(
896       verifier_cb_ctx_create(verifier, pollset, header, claims, audience,
897                              signature, jwt, signed_jwt_len, user_data, cb));
898   return;
899
900 error:
901   if (header != nullptr) jose_header_destroy(header);
902   if (claims != nullptr) grpc_jwt_claims_destroy(claims);
903   cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, nullptr);
904 }
905
906 grpc_jwt_verifier* grpc_jwt_verifier_create(
907     const grpc_jwt_verifier_email_domain_key_url_mapping* mappings,
908     size_t num_mappings) {
909   grpc_jwt_verifier* v =
910       static_cast<grpc_jwt_verifier*>(gpr_zalloc(sizeof(grpc_jwt_verifier)));
911   grpc_httpcli_context_init(&v->http_ctx);
912
913   /* We know at least of one mapping. */
914   v->allocated_mappings = 1 + num_mappings;
915   v->mappings = static_cast<email_key_mapping*>(
916       gpr_malloc(v->allocated_mappings * sizeof(email_key_mapping)));
917   verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN,
918                        GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX);
919   /* User-Provided mappings. */
920   if (mappings != nullptr) {
921     size_t i;
922     for (i = 0; i < num_mappings; i++) {
923       verifier_put_mapping(v, mappings[i].email_domain,
924                            mappings[i].key_url_prefix);
925     }
926   }
927   return v;
928 }
929
930 void grpc_jwt_verifier_destroy(grpc_jwt_verifier* v) {
931   size_t i;
932   if (v == nullptr) return;
933   grpc_httpcli_context_destroy(&v->http_ctx);
934   if (v->mappings != nullptr) {
935     for (i = 0; i < v->num_mappings; i++) {
936       gpr_free(v->mappings[i].email_domain);
937       gpr_free(v->mappings[i].key_url_prefix);
938     }
939     gpr_free(v->mappings);
940   }
941   gpr_free(v);
942 }