blob: 178ce89aa6942b588d04fb17f806215055966272 [file] [log] [blame]
Julien Boeuf8ca294e2016-05-02 14:56:30 -07001/*
2 *
3 * Copyright 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/lib/security/credentials/jwt/jwt_credentials.h"
35
36#include <string.h>
37
38#include "src/core/lib/surface/api_trace.h"
39
40#include <grpc/support/alloc.h>
41#include <grpc/support/log.h>
42#include <grpc/support/string_util.h>
43#include <grpc/support/sync.h>
44
Craig Tillerbd1795c2016-10-31 15:30:00 -070045static void jwt_reset_cache(grpc_exec_ctx *exec_ctx,
46 grpc_service_account_jwt_access_credentials *c) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -070047 if (c->cached.jwt_md != NULL) {
Craig Tillerbd1795c2016-10-31 15:30:00 -070048 grpc_credentials_md_store_unref(exec_ctx, c->cached.jwt_md);
Julien Boeuf8ca294e2016-05-02 14:56:30 -070049 c->cached.jwt_md = NULL;
50 }
51 if (c->cached.service_url != NULL) {
52 gpr_free(c->cached.service_url);
53 c->cached.service_url = NULL;
54 }
55 c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
56}
57
Craig Tillerbd1795c2016-10-31 15:30:00 -070058static void jwt_destruct(grpc_exec_ctx *exec_ctx,
59 grpc_call_credentials *creds) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -070060 grpc_service_account_jwt_access_credentials *c =
61 (grpc_service_account_jwt_access_credentials *)creds;
62 grpc_auth_json_key_destruct(&c->key);
Craig Tillerbd1795c2016-10-31 15:30:00 -070063 jwt_reset_cache(exec_ctx, c);
Julien Boeuf8ca294e2016-05-02 14:56:30 -070064 gpr_mu_destroy(&c->cache_mu);
65}
66
67static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
68 grpc_call_credentials *creds,
David Garcia Quintas2a50dfe2016-05-31 15:09:12 -070069 grpc_polling_entity *pollent,
Julien Boeuf8ca294e2016-05-02 14:56:30 -070070 grpc_auth_metadata_context context,
71 grpc_credentials_metadata_cb cb,
72 void *user_data) {
73 grpc_service_account_jwt_access_credentials *c =
74 (grpc_service_account_jwt_access_credentials *)creds;
75 gpr_timespec refresh_threshold = gpr_time_from_seconds(
76 GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
77
78 /* See if we can return a cached jwt. */
79 grpc_credentials_md_store *jwt_md = NULL;
80 {
81 gpr_mu_lock(&c->cache_mu);
82 if (c->cached.service_url != NULL &&
83 strcmp(c->cached.service_url, context.service_url) == 0 &&
84 c->cached.jwt_md != NULL &&
85 (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration,
86 gpr_now(GPR_CLOCK_REALTIME)),
87 refresh_threshold) > 0)) {
88 jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
89 }
90 gpr_mu_unlock(&c->cache_mu);
91 }
92
93 if (jwt_md == NULL) {
94 char *jwt = NULL;
95 /* Generate a new jwt. */
96 gpr_mu_lock(&c->cache_mu);
Craig Tillerbd1795c2016-10-31 15:30:00 -070097 jwt_reset_cache(exec_ctx, c);
Julien Boeuf8ca294e2016-05-02 14:56:30 -070098 jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url,
99 c->jwt_lifetime, NULL);
100 if (jwt != NULL) {
101 char *md_value;
102 gpr_asprintf(&md_value, "Bearer %s", jwt);
103 gpr_free(jwt);
104 c->cached.jwt_expiration =
105 gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime);
106 c->cached.service_url = gpr_strdup(context.service_url);
107 c->cached.jwt_md = grpc_credentials_md_store_create(1);
108 grpc_credentials_md_store_add_cstrings(
109 c->cached.jwt_md, GRPC_AUTHORIZATION_METADATA_KEY, md_value);
110 gpr_free(md_value);
111 jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
112 }
113 gpr_mu_unlock(&c->cache_mu);
114 }
115
116 if (jwt_md != NULL) {
117 cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries,
Julien Boeufbfc7ed62016-06-04 18:03:42 -0700118 GRPC_CREDENTIALS_OK, NULL);
Craig Tillerbd1795c2016-10-31 15:30:00 -0700119 grpc_credentials_md_store_unref(exec_ctx, jwt_md);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700120 } else {
Julien Boeufb5fe44e2016-06-06 09:50:38 +0200121 cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
122 "Could not generate JWT.");
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700123 }
124}
125
126static grpc_call_credentials_vtable jwt_vtable = {jwt_destruct,
127 jwt_get_request_metadata};
128
129grpc_call_credentials *
130grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
Craig Tillerbd1795c2016-10-31 15:30:00 -0700131 grpc_exec_ctx *exec_ctx, grpc_auth_json_key key,
132 gpr_timespec token_lifetime) {
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700133 grpc_service_account_jwt_access_credentials *c;
134 if (!grpc_auth_json_key_is_valid(&key)) {
135 gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
136 return NULL;
137 }
Craig Tiller6f417882017-02-16 14:09:39 -0800138 c = gpr_zalloc(sizeof(grpc_service_account_jwt_access_credentials));
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700139 c->base.type = GRPC_CALL_CREDENTIALS_TYPE_JWT;
140 gpr_ref_init(&c->base.refcount, 1);
141 c->base.vtable = &jwt_vtable;
142 c->key = key;
143 c->jwt_lifetime = token_lifetime;
144 gpr_mu_init(&c->cache_mu);
Craig Tillerbd1795c2016-10-31 15:30:00 -0700145 jwt_reset_cache(exec_ctx, c);
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700146 return &c->base;
147}
148
yang-g502eb902016-11-09 13:13:13 -0800149static char *redact_private_key(const char *json_key) {
yang-g35d5dfc2016-11-10 14:29:17 -0800150 char *json_copy = gpr_strdup(json_key);
151 grpc_json *json = grpc_json_parse_string(json_copy);
152 if (!json) {
153 gpr_free(json_copy);
154 return gpr_strdup("<Json failed to parse.>");
yang-g502eb902016-11-09 13:13:13 -0800155 }
yang-g35d5dfc2016-11-10 14:29:17 -0800156 const char *redacted = "<redacted>";
157 grpc_json *current = json->child;
158 while (current) {
159 if (current->type == GRPC_JSON_STRING &&
160 strcmp(current->key, "private_key") == 0) {
161 current->value = (char *)redacted;
162 break;
163 }
164 current = current->next;
yang-g502eb902016-11-09 13:13:13 -0800165 }
yang-g35d5dfc2016-11-10 14:29:17 -0800166 char *clean_json = grpc_json_dump_to_string(json, 2);
167 gpr_free(json_copy);
168 grpc_json_destroy(json);
yang-g502eb902016-11-09 13:13:13 -0800169 return clean_json;
170}
171
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700172grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
173 const char *json_key, gpr_timespec token_lifetime, void *reserved) {
yang-g502eb902016-11-09 13:13:13 -0800174 if (grpc_api_trace) {
175 char *clean_json = redact_private_key(json_key);
176 gpr_log(GPR_INFO,
177 "grpc_service_account_jwt_access_credentials_create("
178 "json_key=%s, "
179 "token_lifetime="
180 "gpr_timespec { tv_sec: %" PRId64
181 ", tv_nsec: %d, clock_type: %d }, "
182 "reserved=%p)",
183 clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec,
184 (int)token_lifetime.clock_type, reserved);
185 gpr_free(clean_json);
186 }
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700187 GPR_ASSERT(reserved == NULL);
Craig Tillerbd1795c2016-10-31 15:30:00 -0700188 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
189 grpc_call_credentials *creds =
190 grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
191 &exec_ctx, grpc_auth_json_key_create_from_string(json_key),
192 token_lifetime);
193 grpc_exec_ctx_finish(&exec_ctx);
194 return creds;
Julien Boeuf8ca294e2016-05-02 14:56:30 -0700195}