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