Ejemplo n.º 1
0
static EVP_PKEY *pkey_from_jwk(const grpc_json *json, const char *kty) {
  const grpc_json *key_prop;
  RSA *rsa = NULL;
  EVP_PKEY *result = NULL;

  GPR_ASSERT(kty != NULL && json != NULL);
  if (strcmp(kty, "RSA") != 0) {
    gpr_log(GPR_ERROR, "Unsupported key type %s.", kty);
    goto end;
  }
  rsa = RSA_new();
  if (rsa == NULL) {
    gpr_log(GPR_ERROR, "Could not create rsa key.");
    goto end;
  }
  for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) {
    if (strcmp(key_prop->key, "n") == 0) {
      rsa->n = bignum_from_base64(validate_string_field(key_prop, "n"));
      if (rsa->n == NULL) goto end;
    } else if (strcmp(key_prop->key, "e") == 0) {
      rsa->e = bignum_from_base64(validate_string_field(key_prop, "e"));
      if (rsa->e == NULL) goto end;
    }
  }
  if (rsa->e == NULL || rsa->n == NULL) {
    gpr_log(GPR_ERROR, "Missing RSA public key field.");
    goto end;
  }
  result = EVP_PKEY_new();
  EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */

end:
  if (rsa != NULL) RSA_free(rsa);
  return result;
}
Ejemplo n.º 2
0
/* Takes ownership of json and buffer even in case of failure. */
grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, grpc_slice buffer) {
  grpc_json *cur;
  grpc_jwt_claims *claims = gpr_malloc(sizeof(grpc_jwt_claims));
  memset(claims, 0, sizeof(grpc_jwt_claims));
  claims->json = json;
  claims->buffer = buffer;
  claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME);
  claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME);
  claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME);

  /* Per the spec, all fields are optional. */
  for (cur = json->child; cur != NULL; cur = cur->next) {
    if (strcmp(cur->key, "sub") == 0) {
      claims->sub = validate_string_field(cur, "sub");
      if (claims->sub == NULL) goto error;
    } else if (strcmp(cur->key, "iss") == 0) {
      claims->iss = validate_string_field(cur, "iss");
      if (claims->iss == NULL) goto error;
    } else if (strcmp(cur->key, "aud") == 0) {
      claims->aud = validate_string_field(cur, "aud");
      if (claims->aud == NULL) goto error;
    } else if (strcmp(cur->key, "jti") == 0) {
      claims->jti = validate_string_field(cur, "jti");
      if (claims->jti == NULL) goto error;
    } else if (strcmp(cur->key, "iat") == 0) {
      claims->iat = validate_time_field(cur, "iat");
      if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
        goto error;
    } else if (strcmp(cur->key, "exp") == 0) {
      claims->exp = validate_time_field(cur, "exp");
      if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
        goto error;
    } else if (strcmp(cur->key, "nbf") == 0) {
      claims->nbf = validate_time_field(cur, "nbf");
      if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
        goto error;
    }
  }
  return claims;

error:
  grpc_jwt_claims_destroy(claims);
  return NULL;
}
Ejemplo n.º 3
0
static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
                                       grpc_error *error) {
  const grpc_json *cur;
  verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
  const grpc_http_response *response = &ctx->responses[HTTP_RESPONSE_OPENID];
  grpc_json *json = json_from_http(response);
  grpc_httpcli_request req;
  const char *jwks_uri;

  /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */
  if (json == NULL) goto error;
  cur = find_property_by_name(json, "jwks_uri");
  if (cur == NULL) {
    gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
    goto error;
  }
  jwks_uri = validate_string_field(cur, "jwks_uri");
  if (jwks_uri == NULL) goto error;
  if (strstr(jwks_uri, "https://") != jwks_uri) {
    gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
    goto error;
  }
  jwks_uri += 8;
  req.handshaker = &grpc_httpcli_ssl;
  req.host = gpr_strdup(jwks_uri);
  req.http.path = strchr(jwks_uri, '/');
  if (req.http.path == NULL) {
    req.http.path = "";
  } else {
    *(req.host + (req.http.path - jwks_uri)) = '\0';
  }

  /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
     channel. This would allow us to cancel an authentication query when under
     extreme memory pressure. */
  grpc_resource_quota *resource_quota =
      grpc_resource_quota_create("jwt_verifier");
  grpc_httpcli_get(
      exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
      grpc_closure_create(on_keys_retrieved, ctx),
      &ctx->responses[HTTP_RESPONSE_KEYS]);
  grpc_resource_quota_internal_unref(exec_ctx, resource_quota);
  grpc_json_destroy(json);
  gpr_free(req.host);
  return;

error:
  if (json != NULL) grpc_json_destroy(json);
  ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
  verifier_cb_ctx_destroy(ctx);
}
Ejemplo n.º 4
0
/* Takes ownership of json and buffer. */
static jose_header *jose_header_from_json(grpc_json *json, grpc_slice buffer) {
  grpc_json *cur;
  jose_header *h = gpr_malloc(sizeof(jose_header));
  memset(h, 0, sizeof(jose_header));
  h->buffer = buffer;
  for (cur = json->child; cur != NULL; cur = cur->next) {
    if (strcmp(cur->key, "alg") == 0) {
      /* We only support RSA-1.5 signatures for now.
         Beware of this if we add HMAC support:
         https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
       */
      if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) ||
          evp_md_from_alg(cur->value) == NULL) {
        gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value);
        goto error;
      }
      h->alg = cur->value;
    } else if (strcmp(cur->key, "typ") == 0) {
      h->typ = validate_string_field(cur, "typ");
      if (h->typ == NULL) goto error;
    } else if (strcmp(cur->key, "kid") == 0) {
      h->kid = validate_string_field(cur, "kid");
      if (h->kid == NULL) goto error;
    }
  }
  if (h->alg == NULL) {
    gpr_log(GPR_ERROR, "Missing alg field.");
    goto error;
  }
  grpc_json_destroy(json);
  h->buffer = buffer;
  return h;

error:
  grpc_json_destroy(json);
  jose_header_destroy(h);
  return NULL;
}
Ejemplo n.º 5
0
static void on_openid_config_retrieved(void *user_data,
                                       const grpc_httpcli_response *response) {
  const grpc_json *cur;
  grpc_json *json = json_from_http(response);
  verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
  grpc_httpcli_request req;
  const char *jwks_uri;

  /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time.*/
  if (json == NULL) goto error;
  cur = find_property_by_name(json, "jwks_uri");
  if (cur == NULL) {
    gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
    goto error;
  }
  jwks_uri = validate_string_field(cur, "jwks_uri");
  if (jwks_uri == NULL) goto error;
  if (strstr(jwks_uri, "https://") != jwks_uri) {
    gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
    goto error;
  }
  jwks_uri += 8;
  req.use_ssl = 1;
  req.host = gpr_strdup(jwks_uri);
  req.path = strchr(jwks_uri, '/');
  if (req.path == NULL) {
    req.path = "";
  } else {
    *(req.host + (req.path - jwks_uri)) = '\0';
  }
  grpc_httpcli_get(
      &ctx->verifier->http_ctx, ctx->pollset, &req,
      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
      on_keys_retrieved, ctx);
  grpc_json_destroy(json);
  gpr_free(req.host);
  return;

error:
  if (json != NULL) grpc_json_destroy(json);
  ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
  verifier_cb_ctx_destroy(ctx);
}