blob: f1ae6cecbce0be581f0b4a1bba156b5081406644 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * 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/credentials.h"
35
Julien Boeuff47a5cb2015-02-18 12:24:08 -080036#include <string.h>
37#include <stdio.h>
38
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -070039#include "src/core/channel/channel_args.h"
40#include "src/core/channel/http_client_filter.h"
Julien Boeuff47a5cb2015-02-18 12:24:08 -080041#include "src/core/json/json.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080042#include "src/core/httpcli/httpcli.h"
ctiller18b49ab2014-12-09 14:39:16 -080043#include "src/core/iomgr/iomgr.h"
jboeuf1a809c02014-12-19 15:44:30 -080044#include "src/core/security/json_token.h"
Craig Tiller485d7762015-01-23 12:54:05 -080045#include "src/core/support/string.h"
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -070046
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080047#include <grpc/support/alloc.h>
48#include <grpc/support/log.h>
Masood Malekghassemi701af602015-06-03 15:01:17 -070049#include <grpc/support/string_util.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080050#include <grpc/support/sync.h>
51#include <grpc/support/time.h>
52
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080053/* -- Common. -- */
54
55typedef struct {
56 grpc_credentials *creds;
57 grpc_credentials_metadata_cb cb;
David Garcia Quintas69ba8712015-06-01 11:29:42 -070058 grpc_iomgr_closure *on_simulated_token_fetch_done_closure;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080059 void *user_data;
60} grpc_credentials_metadata_request;
61
62static grpc_credentials_metadata_request *
63grpc_credentials_metadata_request_create(grpc_credentials *creds,
64 grpc_credentials_metadata_cb cb,
65 void *user_data) {
66 grpc_credentials_metadata_request *r =
67 gpr_malloc(sizeof(grpc_credentials_metadata_request));
68 r->creds = grpc_credentials_ref(creds);
69 r->cb = cb;
David Garcia Quintas69ba8712015-06-01 11:29:42 -070070 r->on_simulated_token_fetch_done_closure =
71 gpr_malloc(sizeof(grpc_iomgr_closure));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080072 r->user_data = user_data;
73 return r;
74}
75
76static void grpc_credentials_metadata_request_destroy(
77 grpc_credentials_metadata_request *r) {
78 grpc_credentials_unref(r->creds);
David Garcia Quintas69ba8712015-06-01 11:29:42 -070079 gpr_free(r->on_simulated_token_fetch_done_closure);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080080 gpr_free(r);
81}
82
83grpc_credentials *grpc_credentials_ref(grpc_credentials *creds) {
84 if (creds == NULL) return NULL;
85 gpr_ref(&creds->refcount);
86 return creds;
87}
88
89void grpc_credentials_unref(grpc_credentials *creds) {
90 if (creds == NULL) return;
91 if (gpr_unref(&creds->refcount)) creds->vtable->destroy(creds);
92}
93
94void grpc_credentials_release(grpc_credentials *creds) {
95 grpc_credentials_unref(creds);
96}
97
98int grpc_credentials_has_request_metadata(grpc_credentials *creds) {
99 if (creds == NULL) return 0;
100 return creds->vtable->has_request_metadata(creds);
101}
102
103int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) {
104 if (creds == NULL) return 0;
105 return creds->vtable->has_request_metadata_only(creds);
106}
107
108void grpc_credentials_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800109 const char *service_url,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800110 grpc_credentials_metadata_cb cb,
111 void *user_data) {
nnoble0c475f02014-12-05 15:37:39 -0800112 if (creds == NULL || !grpc_credentials_has_request_metadata(creds) ||
113 creds->vtable->get_request_metadata == NULL) {
114 if (cb != NULL) {
115 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
116 }
117 return;
118 }
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800119 creds->vtable->get_request_metadata(creds, service_url, cb, user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800120}
121
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700122grpc_security_status grpc_credentials_create_security_connector(
123 grpc_credentials *creds, const char *target, const grpc_channel_args *args,
124 grpc_credentials *request_metadata_creds,
125 grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
126 *new_args = NULL;
127 if (creds == NULL || creds->vtable->create_security_connector == NULL ||
128 grpc_credentials_has_request_metadata_only(creds)) {
129 gpr_log(GPR_ERROR,
130 "Invalid credentials for creating a security connector.");
131 return GRPC_SECURITY_ERROR;
132 }
133 return creds->vtable->create_security_connector(
134 creds, target, args, request_metadata_creds, sc, new_args);
Craig Tillerc4885ed2015-04-14 09:51:28 -0700135}
136
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800137void grpc_server_credentials_release(grpc_server_credentials *creds) {
138 if (creds == NULL) return;
139 creds->vtable->destroy(creds);
140}
141
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700142grpc_security_status grpc_server_credentials_create_security_connector(
143 grpc_server_credentials *creds, grpc_security_connector **sc) {
144 if (creds == NULL || creds->vtable->create_security_connector == NULL) {
145 gpr_log(GPR_ERROR, "Server credentials cannot create security context.");
146 return GRPC_SECURITY_ERROR;
147 }
148 return creds->vtable->create_security_connector(creds, sc);
149}
150
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800151/* -- Ssl credentials. -- */
152
153typedef struct {
154 grpc_credentials base;
155 grpc_ssl_config config;
156} grpc_ssl_credentials;
157
158typedef struct {
159 grpc_server_credentials base;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800160 grpc_ssl_server_config config;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800161} grpc_ssl_server_credentials;
162
163static void ssl_destroy(grpc_credentials *creds) {
164 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
165 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
166 if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
167 if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
168 gpr_free(creds);
169}
170
171static void ssl_server_destroy(grpc_server_credentials *creds) {
172 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800173 size_t i;
174 for (i = 0; i < c->config.num_key_cert_pairs; i++) {
175 if (c->config.pem_private_keys[i] != NULL) {
176 gpr_free(c->config.pem_private_keys[i]);
177 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800178 if (c->config.pem_cert_chains[i] != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800179 gpr_free(c->config.pem_cert_chains[i]);
180 }
181 }
182 if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys);
183 if (c->config.pem_private_keys_sizes != NULL) {
184 gpr_free(c->config.pem_private_keys_sizes);
185 }
186 if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains);
187 if (c->config.pem_cert_chains_sizes != NULL) {
188 gpr_free(c->config.pem_cert_chains_sizes);
189 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800190 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800191 gpr_free(creds);
192}
193
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100194static int ssl_has_request_metadata(const grpc_credentials *creds) {
195 return 0;
196}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800197
198static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
199 return 0;
200}
201
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700202static grpc_security_status ssl_create_security_connector(
203 grpc_credentials *creds, const char *target, const grpc_channel_args *args,
204 grpc_credentials *request_metadata_creds,
205 grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
206 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
207 grpc_security_status status = GRPC_SECURITY_OK;
208 size_t i = 0;
209 const char *overridden_target_name = NULL;
210 grpc_arg arg;
211
212 for (i = 0; args && i < args->num_args; i++) {
213 grpc_arg *arg = &args->args[i];
214 if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
215 arg->type == GRPC_ARG_STRING) {
216 overridden_target_name = arg->value.string;
217 break;
218 }
219 }
220 status = grpc_ssl_channel_security_connector_create(
221 request_metadata_creds, &c->config, target, overridden_target_name, sc);
222 if (status != GRPC_SECURITY_OK) {
223 return status;
224 }
225 arg.type = GRPC_ARG_STRING;
226 arg.key = GRPC_ARG_HTTP2_SCHEME;
227 arg.value.string = "https";
228 *new_args = grpc_channel_args_copy_and_add(args, &arg);
229 return status;
230}
231
232static grpc_security_status ssl_server_create_security_connector(
233 grpc_server_credentials *creds, grpc_security_connector **sc) {
234 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
235 return grpc_ssl_server_security_connector_create(&c->config, sc);
236}
237
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800238static grpc_credentials_vtable ssl_vtable = {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700239 ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL,
240 ssl_create_security_connector};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800241
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700242static grpc_server_credentials_vtable ssl_server_vtable = {
243 ssl_server_destroy, ssl_server_create_security_connector};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800244
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800245static void ssl_copy_key_material(const char *input, unsigned char **output,
246 size_t *output_size) {
247 *output_size = strlen(input);
248 *output = gpr_malloc(*output_size);
249 memcpy(*output, input, *output_size);
250}
251
252static void ssl_build_config(const char *pem_root_certs,
253 grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800254 grpc_ssl_config *config) {
Julien Boeuf026a4172015-02-02 18:36:37 -0800255 if (pem_root_certs != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800256 ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
257 &config->pem_root_certs_size);
258 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800259 if (pem_key_cert_pair != NULL) {
260 GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
261 GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
262 ssl_copy_key_material(pem_key_cert_pair->private_key,
263 &config->pem_private_key,
264 &config->pem_private_key_size);
265 ssl_copy_key_material(pem_key_cert_pair->cert_chain,
266 &config->pem_cert_chain,
267 &config->pem_cert_chain_size);
268 }
269}
270
271static void ssl_build_server_config(
272 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
273 size_t num_key_cert_pairs, grpc_ssl_server_config *config) {
274 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800275 if (pem_root_certs != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800276 ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
277 &config->pem_root_certs_size);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800278 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800279 if (num_key_cert_pairs > 0) {
280 GPR_ASSERT(pem_key_cert_pairs != NULL);
281 config->pem_private_keys =
282 gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
283 config->pem_cert_chains =
284 gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
285 config->pem_private_keys_sizes =
286 gpr_malloc(num_key_cert_pairs * sizeof(size_t));
287 config->pem_cert_chains_sizes =
288 gpr_malloc(num_key_cert_pairs * sizeof(size_t));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800289 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800290 config->num_key_cert_pairs = num_key_cert_pairs;
291 for (i = 0; i < num_key_cert_pairs; i++) {
292 GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
293 GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
294 ssl_copy_key_material(pem_key_cert_pairs[i].private_key,
295 &config->pem_private_keys[i],
296 &config->pem_private_keys_sizes[i]);
297 ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain,
298 &config->pem_cert_chains[i],
299 &config->pem_cert_chains_sizes[i]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800300 }
301}
302
303grpc_credentials *grpc_ssl_credentials_create(
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800304 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800305 grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
306 memset(c, 0, sizeof(grpc_ssl_credentials));
307 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
308 c->base.vtable = &ssl_vtable;
309 gpr_ref_init(&c->base.refcount, 1);
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800310 ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800311 return &c->base;
312}
313
314grpc_server_credentials *grpc_ssl_server_credentials_create(
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800315 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
316 size_t num_key_cert_pairs) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800317 grpc_ssl_server_credentials *c =
318 gpr_malloc(sizeof(grpc_ssl_server_credentials));
319 memset(c, 0, sizeof(grpc_ssl_server_credentials));
320 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
321 c->base.vtable = &ssl_server_vtable;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800322 ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
323 num_key_cert_pairs, &c->config);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800324 return &c->base;
325}
326
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800327/* -- Jwt credentials -- */
328
329typedef struct {
330 grpc_credentials base;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800331
332 /* Have a simple cache for now with just 1 entry. We could have a map based on
333 the service_url for a more sophisticated one. */
334 gpr_mu cache_mu;
335 struct {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700336 grpc_credentials_md_store *jwt_md;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800337 char *service_url;
338 gpr_timespec jwt_expiration;
339 } cached;
340
341 grpc_auth_json_key key;
342 gpr_timespec jwt_lifetime;
343} grpc_jwt_credentials;
344
345static void jwt_reset_cache(grpc_jwt_credentials *c) {
346 if (c->cached.jwt_md != NULL) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700347 grpc_credentials_md_store_unref(c->cached.jwt_md);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800348 c->cached.jwt_md = NULL;
349 }
350 if (c->cached.service_url != NULL) {
351 gpr_free(c->cached.service_url);
352 c->cached.service_url = NULL;
353 }
354 c->cached.jwt_expiration = gpr_inf_past;
355}
356
357static void jwt_destroy(grpc_credentials *creds) {
358 grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds;
359 grpc_auth_json_key_destruct(&c->key);
360 jwt_reset_cache(c);
361 gpr_mu_destroy(&c->cache_mu);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800362 gpr_free(c);
363}
364
365static int jwt_has_request_metadata(const grpc_credentials *creds) { return 1; }
366
367static int jwt_has_request_metadata_only(const grpc_credentials *creds) {
368 return 1;
369}
370
371
372static void jwt_get_request_metadata(grpc_credentials *creds,
373 const char *service_url,
374 grpc_credentials_metadata_cb cb,
375 void *user_data) {
376 grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds;
377 gpr_timespec refresh_threshold = {GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
378 0};
379
380 /* See if we can return a cached jwt. */
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700381 grpc_credentials_md_store *jwt_md = NULL;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800382 {
383 gpr_mu_lock(&c->cache_mu);
384 if (c->cached.service_url != NULL &&
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -0800385 strcmp(c->cached.service_url, service_url) == 0 &&
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800386 c->cached.jwt_md != NULL &&
387 (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now()),
388 refresh_threshold) > 0)) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700389 jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800390 }
391 gpr_mu_unlock(&c->cache_mu);
392 }
393
394 if (jwt_md == NULL) {
395 char *jwt = NULL;
396 /* Generate a new jwt. */
397 gpr_mu_lock(&c->cache_mu);
398 jwt_reset_cache(c);
399 jwt = grpc_jwt_encode_and_sign(&c->key, service_url, c->jwt_lifetime, NULL);
400 if (jwt != NULL) {
401 char *md_value;
402 gpr_asprintf(&md_value, "Bearer %s", jwt);
403 gpr_free(jwt);
404 c->cached.jwt_expiration = gpr_time_add(gpr_now(), c->jwt_lifetime);
405 c->cached.service_url = gpr_strdup(service_url);
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700406 c->cached.jwt_md = grpc_credentials_md_store_create(1);
407 grpc_credentials_md_store_add_cstrings(
408 c->cached.jwt_md, GRPC_AUTHORIZATION_METADATA_KEY, md_value);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800409 gpr_free(md_value);
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700410 jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800411 }
412 gpr_mu_unlock(&c->cache_mu);
413 }
414
415 if (jwt_md != NULL) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700416 cb(user_data, jwt_md->entries, jwt_md->num_entries, GRPC_CREDENTIALS_OK);
417 grpc_credentials_md_store_unref(jwt_md);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800418 } else {
419 cb(user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
420 }
421}
422
423static grpc_credentials_vtable jwt_vtable = {
424 jwt_destroy, jwt_has_request_metadata, jwt_has_request_metadata_only,
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700425 jwt_get_request_metadata, NULL};
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800426
427grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
428 gpr_timespec token_lifetime) {
429 grpc_jwt_credentials *c;
430 grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
431 if (!grpc_auth_json_key_is_valid(&key)) {
432 gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
433 return NULL;
434 }
435 c = gpr_malloc(sizeof(grpc_jwt_credentials));
436 memset(c, 0, sizeof(grpc_jwt_credentials));
437 c->base.type = GRPC_CREDENTIALS_TYPE_JWT;
438 gpr_ref_init(&c->base.refcount, 1);
439 c->base.vtable = &jwt_vtable;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800440 c->key = key;
441 c->jwt_lifetime = token_lifetime;
442 gpr_mu_init(&c->cache_mu);
443 jwt_reset_cache(c);
444 return &c->base;
445}
446
jboeuf1a809c02014-12-19 15:44:30 -0800447/* -- Oauth2TokenFetcher credentials -- */
448
449/* This object is a base for credentials that need to acquire an oauth2 token
450 from an http service. */
451
452typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req,
453 grpc_httpcli_response_cb response_cb,
454 gpr_timespec deadline);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800455
456typedef struct {
457 grpc_credentials base;
458 gpr_mu mu;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700459 grpc_credentials_md_store *access_token_md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800460 gpr_timespec token_expiration;
jboeuf1a809c02014-12-19 15:44:30 -0800461 grpc_fetch_oauth2_func fetch_func;
462} grpc_oauth2_token_fetcher_credentials;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800463
jboeuf1a809c02014-12-19 15:44:30 -0800464static void oauth2_token_fetcher_destroy(grpc_credentials *creds) {
465 grpc_oauth2_token_fetcher_credentials *c =
466 (grpc_oauth2_token_fetcher_credentials *)creds;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700467 grpc_credentials_md_store_unref(c->access_token_md);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800468 gpr_mu_destroy(&c->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800469 gpr_free(c);
470}
471
jboeuf1a809c02014-12-19 15:44:30 -0800472static int oauth2_token_fetcher_has_request_metadata(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800473 const grpc_credentials *creds) {
474 return 1;
475}
476
jboeuf1a809c02014-12-19 15:44:30 -0800477static int oauth2_token_fetcher_has_request_metadata_only(
478 const grpc_credentials *creds) {
479 return 1;
480}
481
482grpc_credentials_status
483grpc_oauth2_token_fetcher_credentials_parse_server_response(
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700484 const grpc_httpcli_response *response,
485 grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800486 char *null_terminated_body = NULL;
487 char *new_access_token = NULL;
488 grpc_credentials_status status = GRPC_CREDENTIALS_OK;
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800489 grpc_json *json = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800490
Julien Boeuf90bd7c42015-02-18 18:51:16 -0800491 if (response == NULL) {
492 gpr_log(GPR_ERROR, "Received NULL response.");
493 status = GRPC_CREDENTIALS_ERROR;
494 goto end;
495 }
496
jboeuf1a809c02014-12-19 15:44:30 -0800497 if (response->body_length > 0) {
498 null_terminated_body = gpr_malloc(response->body_length + 1);
499 null_terminated_body[response->body_length] = '\0';
500 memcpy(null_terminated_body, response->body, response->body_length);
501 }
502
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800503 if (response->status != 200) {
jboeuf1a809c02014-12-19 15:44:30 -0800504 gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
505 response->status,
506 null_terminated_body != NULL ? null_terminated_body : "");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800507 status = GRPC_CREDENTIALS_ERROR;
508 goto end;
509 } else {
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800510 grpc_json *access_token = NULL;
511 grpc_json *token_type = NULL;
512 grpc_json *expires_in = NULL;
513 grpc_json *ptr;
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800514 json = grpc_json_parse_string(null_terminated_body);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800515 if (json == NULL) {
516 gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
517 status = GRPC_CREDENTIALS_ERROR;
518 goto end;
519 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800520 if (json->type != GRPC_JSON_OBJECT) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800521 gpr_log(GPR_ERROR, "Response should be a JSON object");
522 status = GRPC_CREDENTIALS_ERROR;
523 goto end;
524 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800525 for (ptr = json->child; ptr; ptr = ptr->next) {
526 if (strcmp(ptr->key, "access_token") == 0) {
527 access_token = ptr;
528 } else if (strcmp(ptr->key, "token_type") == 0) {
529 token_type = ptr;
530 } else if (strcmp(ptr->key, "expires_in") == 0) {
531 expires_in = ptr;
532 }
533 }
534 if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800535 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
536 status = GRPC_CREDENTIALS_ERROR;
537 goto end;
538 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800539 if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800540 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
541 status = GRPC_CREDENTIALS_ERROR;
542 goto end;
543 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800544 if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800545 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
546 status = GRPC_CREDENTIALS_ERROR;
547 goto end;
548 }
Nicolas Noblefee065c2015-01-26 11:41:12 -0800549 gpr_asprintf(&new_access_token, "%s %s", token_type->value,
550 access_token->value);
Nicolas Noblee04455a2015-01-26 17:01:29 -0800551 token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800552 token_lifetime->tv_nsec = 0;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700553 if (*token_md != NULL) grpc_credentials_md_store_unref(*token_md);
554 *token_md = grpc_credentials_md_store_create(1);
555 grpc_credentials_md_store_add_cstrings(
556 *token_md, GRPC_AUTHORIZATION_METADATA_KEY, new_access_token);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800557 status = GRPC_CREDENTIALS_OK;
558 }
559
560end:
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700561 if (status != GRPC_CREDENTIALS_OK && (*token_md != NULL)) {
562 grpc_credentials_md_store_unref(*token_md);
563 *token_md = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800564 }
565 if (null_terminated_body != NULL) gpr_free(null_terminated_body);
566 if (new_access_token != NULL) gpr_free(new_access_token);
Nicolas Noble8c2be9b2015-01-27 14:21:18 -0800567 if (json != NULL) grpc_json_destroy(json);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800568 return status;
569}
570
jboeuf1a809c02014-12-19 15:44:30 -0800571static void on_oauth2_token_fetcher_http_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800572 void *user_data, const grpc_httpcli_response *response) {
573 grpc_credentials_metadata_request *r =
574 (grpc_credentials_metadata_request *)user_data;
jboeuf1a809c02014-12-19 15:44:30 -0800575 grpc_oauth2_token_fetcher_credentials *c =
576 (grpc_oauth2_token_fetcher_credentials *)r->creds;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800577 gpr_timespec token_lifetime;
578 grpc_credentials_status status;
579
580 gpr_mu_lock(&c->mu);
jboeuf1a809c02014-12-19 15:44:30 -0800581 status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700582 response, &c->access_token_md, &token_lifetime);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800583 if (status == GRPC_CREDENTIALS_OK) {
584 c->token_expiration = gpr_time_add(gpr_now(), token_lifetime);
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700585 r->cb(r->user_data, c->access_token_md->entries,
586 c->access_token_md->num_entries, status);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800587 } else {
588 c->token_expiration = gpr_inf_past;
589 r->cb(r->user_data, NULL, 0, status);
590 }
591 gpr_mu_unlock(&c->mu);
592 grpc_credentials_metadata_request_destroy(r);
593}
594
jboeuf1a809c02014-12-19 15:44:30 -0800595static void oauth2_token_fetcher_get_request_metadata(
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800596 grpc_credentials *creds, const char *service_url,
597 grpc_credentials_metadata_cb cb, void *user_data) {
jboeuf1a809c02014-12-19 15:44:30 -0800598 grpc_oauth2_token_fetcher_credentials *c =
599 (grpc_oauth2_token_fetcher_credentials *)creds;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800600 gpr_timespec refresh_threshold = {GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
jboeuf1a809c02014-12-19 15:44:30 -0800601 0};
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700602 grpc_credentials_md_store *cached_access_token_md = NULL;
jboeuf1a809c02014-12-19 15:44:30 -0800603 {
604 gpr_mu_lock(&c->mu);
605 if (c->access_token_md != NULL &&
606 (gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()),
607 refresh_threshold) > 0)) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700608 cached_access_token_md = grpc_credentials_md_store_ref(c->access_token_md);
jboeuf1a809c02014-12-19 15:44:30 -0800609 }
610 gpr_mu_unlock(&c->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800611 }
jboeuf1a809c02014-12-19 15:44:30 -0800612 if (cached_access_token_md != NULL) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700613 cb(user_data, cached_access_token_md->entries,
614 cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK);
615 grpc_credentials_md_store_unref(cached_access_token_md);
jboeuf1a809c02014-12-19 15:44:30 -0800616 } else {
617 c->fetch_func(
618 grpc_credentials_metadata_request_create(creds, cb, user_data),
619 on_oauth2_token_fetcher_http_response,
620 gpr_time_add(gpr_now(), refresh_threshold));
621 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800622}
623
jboeuf1a809c02014-12-19 15:44:30 -0800624static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
625 grpc_fetch_oauth2_func fetch_func) {
626 memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800627 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800628 gpr_ref_init(&c->base.refcount, 1);
629 gpr_mu_init(&c->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800630 c->token_expiration = gpr_inf_past;
jboeuf1a809c02014-12-19 15:44:30 -0800631 c->fetch_func = fetch_func;
632}
633
634/* -- ComputeEngine credentials. -- */
635
636static grpc_credentials_vtable compute_engine_vtable = {
637 oauth2_token_fetcher_destroy, oauth2_token_fetcher_has_request_metadata,
638 oauth2_token_fetcher_has_request_metadata_only,
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700639 oauth2_token_fetcher_get_request_metadata, NULL};
jboeuf1a809c02014-12-19 15:44:30 -0800640
641static void compute_engine_fetch_oauth2(
642 grpc_credentials_metadata_request *metadata_req,
643 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
644 grpc_httpcli_header header = {"Metadata-Flavor", "Google"};
645 grpc_httpcli_request request;
646 memset(&request, 0, sizeof(grpc_httpcli_request));
647 request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
648 request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
649 request.hdr_count = 1;
650 request.hdrs = &header;
651 grpc_httpcli_get(&request, deadline, response_cb, metadata_req);
652}
653
654grpc_credentials *grpc_compute_engine_credentials_create(void) {
655 grpc_oauth2_token_fetcher_credentials *c =
656 gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
657 init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
658 c->base.vtable = &compute_engine_vtable;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800659 return &c->base;
660}
661
jboeuf1a809c02014-12-19 15:44:30 -0800662/* -- ServiceAccount credentials. -- */
663
664typedef struct {
665 grpc_oauth2_token_fetcher_credentials base;
666 grpc_auth_json_key key;
667 char *scope;
668 gpr_timespec token_lifetime;
669} grpc_service_account_credentials;
670
671static void service_account_destroy(grpc_credentials *creds) {
672 grpc_service_account_credentials *c =
673 (grpc_service_account_credentials *)creds;
674 if (c->scope != NULL) gpr_free(c->scope);
675 grpc_auth_json_key_destruct(&c->key);
676 oauth2_token_fetcher_destroy(&c->base.base);
677}
678
679static grpc_credentials_vtable service_account_vtable = {
680 service_account_destroy, oauth2_token_fetcher_has_request_metadata,
681 oauth2_token_fetcher_has_request_metadata_only,
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700682 oauth2_token_fetcher_get_request_metadata, NULL};
jboeuf1a809c02014-12-19 15:44:30 -0800683
684static void service_account_fetch_oauth2(
685 grpc_credentials_metadata_request *metadata_req,
686 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
687 grpc_service_account_credentials *c =
688 (grpc_service_account_credentials *)metadata_req->creds;
689 grpc_httpcli_header header = {"Content-Type",
690 "application/x-www-form-urlencoded"};
691 grpc_httpcli_request request;
692 char *body = NULL;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800693 char *jwt = grpc_jwt_encode_and_sign(&c->key, GRPC_JWT_OAUTH2_AUDIENCE,
694 c->token_lifetime, c->scope);
jboeuf1a809c02014-12-19 15:44:30 -0800695 if (jwt == NULL) {
696 grpc_httpcli_response response;
697 memset(&response, 0, sizeof(grpc_httpcli_response));
698 response.status = 400; /* Invalid request. */
699 gpr_log(GPR_ERROR, "Could not create signed jwt.");
700 /* Do not even send the request, just call the response callback. */
701 response_cb(metadata_req, &response);
702 return;
703 }
Craig Tillerbe6a3552015-01-23 13:04:45 -0800704 gpr_asprintf(&body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt);
jboeuf1a809c02014-12-19 15:44:30 -0800705 memset(&request, 0, sizeof(grpc_httpcli_request));
Julien Boeuf9835cf02015-03-09 16:56:44 -0700706 request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
707 request.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
jboeuf1a809c02014-12-19 15:44:30 -0800708 request.hdr_count = 1;
709 request.hdrs = &header;
710 request.use_ssl = 1;
711 grpc_httpcli_post(&request, body, strlen(body), deadline, response_cb,
712 metadata_req);
713 gpr_free(body);
714 gpr_free(jwt);
715}
716
717grpc_credentials *grpc_service_account_credentials_create(
718 const char *json_key, const char *scope, gpr_timespec token_lifetime) {
719 grpc_service_account_credentials *c;
720 grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
721
722 if (scope == NULL || (strlen(scope) == 0) ||
723 !grpc_auth_json_key_is_valid(&key)) {
724 gpr_log(GPR_ERROR,
725 "Invalid input for service account credentials creation");
726 return NULL;
727 }
728 c = gpr_malloc(sizeof(grpc_service_account_credentials));
729 memset(c, 0, sizeof(grpc_service_account_credentials));
730 init_oauth2_token_fetcher(&c->base, service_account_fetch_oauth2);
731 c->base.base.vtable = &service_account_vtable;
732 c->scope = gpr_strdup(scope);
733 c->key = key;
734 c->token_lifetime = token_lifetime;
735 return &c->base.base;
736}
737
Julien Boeuf9835cf02015-03-09 16:56:44 -0700738/* -- RefreshToken credentials. -- */
739
740typedef struct {
741 grpc_oauth2_token_fetcher_credentials base;
742 grpc_auth_refresh_token refresh_token;
743} grpc_refresh_token_credentials;
744
745static void refresh_token_destroy(grpc_credentials *creds) {
746 grpc_refresh_token_credentials *c =
747 (grpc_refresh_token_credentials *)creds;
748 grpc_auth_refresh_token_destruct(&c->refresh_token);
749 oauth2_token_fetcher_destroy(&c->base.base);
750}
751
752static grpc_credentials_vtable refresh_token_vtable = {
753 refresh_token_destroy, oauth2_token_fetcher_has_request_metadata,
754 oauth2_token_fetcher_has_request_metadata_only,
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700755 oauth2_token_fetcher_get_request_metadata, NULL};
Julien Boeuf9835cf02015-03-09 16:56:44 -0700756
757static void refresh_token_fetch_oauth2(
758 grpc_credentials_metadata_request *metadata_req,
759 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
760 grpc_refresh_token_credentials *c =
761 (grpc_refresh_token_credentials *)metadata_req->creds;
762 grpc_httpcli_header header = {"Content-Type",
763 "application/x-www-form-urlencoded"};
764 grpc_httpcli_request request;
765 char *body = NULL;
766 gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
767 c->refresh_token.client_id, c->refresh_token.client_secret,
768 c->refresh_token.refresh_token);
769 memset(&request, 0, sizeof(grpc_httpcli_request));
770 request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
771 request.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
772 request.hdr_count = 1;
773 request.hdrs = &header;
774 request.use_ssl = 1;
775 grpc_httpcli_post(&request, body, strlen(body), deadline, response_cb,
776 metadata_req);
777 gpr_free(body);
778}
779
780grpc_credentials *grpc_refresh_token_credentials_create(
781 const char *json_refresh_token) {
782 grpc_refresh_token_credentials *c;
783 grpc_auth_refresh_token refresh_token =
784 grpc_auth_refresh_token_create_from_string(json_refresh_token);
785
786 if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
787 gpr_log(GPR_ERROR,
788 "Invalid input for refresh token credentials creation");
789 return NULL;
790 }
791 c = gpr_malloc(sizeof(grpc_refresh_token_credentials));
792 memset(c, 0, sizeof(grpc_refresh_token_credentials));
793 init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2);
794 c->base.base.vtable = &refresh_token_vtable;
795 c->refresh_token = refresh_token;
796 return &c->base.base;
797}
798
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800799/* -- Fake Oauth2 credentials. -- */
800
801typedef struct {
802 grpc_credentials base;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700803 grpc_credentials_md_store *access_token_md;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800804 int is_async;
805} grpc_fake_oauth2_credentials;
806
807static void fake_oauth2_destroy(grpc_credentials *creds) {
808 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700809 grpc_credentials_md_store_unref(c->access_token_md);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800810 gpr_free(c);
811}
812
813static int fake_oauth2_has_request_metadata(const grpc_credentials *creds) {
814 return 1;
815}
816
817static int fake_oauth2_has_request_metadata_only(
818 const grpc_credentials *creds) {
819 return 1;
820}
821
ctiller58393c22015-01-07 14:03:30 -0800822void on_simulated_token_fetch_done(void *user_data, int success) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800823 grpc_credentials_metadata_request *r =
824 (grpc_credentials_metadata_request *)user_data;
825 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)r->creds;
ctiller58393c22015-01-07 14:03:30 -0800826 GPR_ASSERT(success);
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700827 r->cb(r->user_data, c->access_token_md->entries,
828 c->access_token_md->num_entries, GRPC_CREDENTIALS_OK);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800829 grpc_credentials_metadata_request_destroy(r);
830}
831
832static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800833 const char *service_url,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800834 grpc_credentials_metadata_cb cb,
835 void *user_data) {
836 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
837
838 if (c->is_async) {
David Garcia Quintas69ba8712015-06-01 11:29:42 -0700839 grpc_credentials_metadata_request *cb_arg =
David Garcia Quintas8b8cce02015-05-31 12:39:40 -0700840 grpc_credentials_metadata_request_create(creds, cb, user_data);
David Garcia Quintas69ba8712015-06-01 11:29:42 -0700841 grpc_iomgr_closure_init(cb_arg->on_simulated_token_fetch_done_closure,
842 on_simulated_token_fetch_done, cb_arg);
843 grpc_iomgr_add_callback(cb_arg->on_simulated_token_fetch_done_closure);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800844 } else {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700845 cb(user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800846 }
847}
848
849static grpc_credentials_vtable fake_oauth2_vtable = {
850 fake_oauth2_destroy, fake_oauth2_has_request_metadata,
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700851 fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata,
852 NULL};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800853
854grpc_credentials *grpc_fake_oauth2_credentials_create(
855 const char *token_md_value, int is_async) {
856 grpc_fake_oauth2_credentials *c =
857 gpr_malloc(sizeof(grpc_fake_oauth2_credentials));
858 memset(c, 0, sizeof(grpc_fake_oauth2_credentials));
859 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
860 c->base.vtable = &fake_oauth2_vtable;
861 gpr_ref_init(&c->base.refcount, 1);
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700862 c->access_token_md = grpc_credentials_md_store_create(1);
863 grpc_credentials_md_store_add_cstrings(
864 c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800865 c->is_async = is_async;
866 return &c->base;
867}
868
869/* -- Fake transport security credentials. -- */
870
871static void fake_transport_security_credentials_destroy(
872 grpc_credentials *creds) {
873 gpr_free(creds);
874}
875
876static void fake_transport_security_server_credentials_destroy(
877 grpc_server_credentials *creds) {
878 gpr_free(creds);
879}
880
881static int fake_transport_security_has_request_metadata(
882 const grpc_credentials *creds) {
883 return 0;
884}
885
886static int fake_transport_security_has_request_metadata_only(
887 const grpc_credentials *creds) {
888 return 0;
889}
890
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700891static grpc_security_status
892fake_transport_security_create_security_connector(
893 grpc_credentials *c, const char *target, const grpc_channel_args *args,
894 grpc_credentials *request_metadata_creds,
895 grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
896 *sc = grpc_fake_channel_security_connector_create(request_metadata_creds, 1);
897 return GRPC_SECURITY_OK;
898}
899
900static grpc_security_status
901fake_transport_security_server_create_security_connector(
902 grpc_server_credentials *c, grpc_security_connector **sc) {
903 *sc = grpc_fake_server_security_connector_create();
904 return GRPC_SECURITY_OK;
905}
906
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800907static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
908 fake_transport_security_credentials_destroy,
909 fake_transport_security_has_request_metadata,
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700910 fake_transport_security_has_request_metadata_only, NULL,
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700911 fake_transport_security_create_security_connector};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800912
913static grpc_server_credentials_vtable
914 fake_transport_security_server_credentials_vtable = {
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700915 fake_transport_security_server_credentials_destroy,
916 fake_transport_security_server_create_security_connector};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800917
918grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
919 grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
920 memset(c, 0, sizeof(grpc_credentials));
921 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
922 c->vtable = &fake_transport_security_credentials_vtable;
923 gpr_ref_init(&c->refcount, 1);
924 return c;
925}
926
Craig Tiller3eef2c42015-01-15 11:37:54 -0800927grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
928 void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800929 grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
930 memset(c, 0, sizeof(grpc_server_credentials));
931 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
932 c->vtable = &fake_transport_security_server_credentials_vtable;
933 return c;
934}
935
nnoble0c475f02014-12-05 15:37:39 -0800936/* -- Composite credentials. -- */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800937
nnoble0c475f02014-12-05 15:37:39 -0800938typedef struct {
939 grpc_credentials base;
940 grpc_credentials_array inner;
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700941 grpc_credentials *connector_creds;
nnoble0c475f02014-12-05 15:37:39 -0800942} grpc_composite_credentials;
943
944typedef struct {
945 grpc_composite_credentials *composite_creds;
946 size_t creds_index;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700947 grpc_credentials_md_store *md_elems;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800948 char *service_url;
nnoble0c475f02014-12-05 15:37:39 -0800949 void *user_data;
950 grpc_credentials_metadata_cb cb;
951} grpc_composite_credentials_metadata_context;
952
953static void composite_destroy(grpc_credentials *creds) {
954 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
955 size_t i;
956 for (i = 0; i < c->inner.num_creds; i++) {
957 grpc_credentials_unref(c->inner.creds_array[i]);
958 }
959 gpr_free(c->inner.creds_array);
960 gpr_free(creds);
961}
962
963static int composite_has_request_metadata(const grpc_credentials *creds) {
964 const grpc_composite_credentials *c =
965 (const grpc_composite_credentials *)creds;
966 size_t i;
967 for (i = 0; i < c->inner.num_creds; i++) {
968 if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) {
969 return 1;
970 }
971 }
972 return 0;
973}
974
975static int composite_has_request_metadata_only(const grpc_credentials *creds) {
976 const grpc_composite_credentials *c =
977 (const grpc_composite_credentials *)creds;
978 size_t i;
979 for (i = 0; i < c->inner.num_creds; i++) {
980 if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) {
981 return 0;
982 }
983 }
984 return 1;
985}
986
987static void composite_md_context_destroy(
988 grpc_composite_credentials_metadata_context *ctx) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700989 grpc_credentials_md_store_unref(ctx->md_elems);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800990 if (ctx->service_url != NULL) gpr_free(ctx->service_url);
nnoble0c475f02014-12-05 15:37:39 -0800991 gpr_free(ctx);
992}
993
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700994static void composite_metadata_cb(void *user_data,
995 grpc_credentials_md *md_elems, size_t num_md,
nnoble0c475f02014-12-05 15:37:39 -0800996 grpc_credentials_status status) {
997 grpc_composite_credentials_metadata_context *ctx =
998 (grpc_composite_credentials_metadata_context *)user_data;
nnoble0c475f02014-12-05 15:37:39 -0800999 if (status != GRPC_CREDENTIALS_OK) {
1000 ctx->cb(ctx->user_data, NULL, 0, status);
1001 return;
1002 }
1003
1004 /* Copy the metadata in the context. */
1005 if (num_md > 0) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001006 size_t i;
nnoble0c475f02014-12-05 15:37:39 -08001007 for (i = 0; i < num_md; i++) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001008 grpc_credentials_md_store_add(ctx->md_elems, md_elems[i].key,
1009 md_elems[i].value);
nnoble0c475f02014-12-05 15:37:39 -08001010 }
nnoble0c475f02014-12-05 15:37:39 -08001011 }
1012
1013 /* See if we need to get some more metadata. */
1014 while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
1015 grpc_credentials *inner_creds =
1016 ctx->composite_creds->inner.creds_array[ctx->creds_index++];
1017 if (grpc_credentials_has_request_metadata(inner_creds)) {
Julien Boeuff47a5cb2015-02-18 12:24:08 -08001018 grpc_credentials_get_request_metadata(inner_creds, ctx->service_url,
1019 composite_metadata_cb, ctx);
nnoble0c475f02014-12-05 15:37:39 -08001020 return;
1021 }
1022 }
1023
1024 /* We're done!. */
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001025 ctx->cb(ctx->user_data, ctx->md_elems->entries, ctx->md_elems->num_entries,
1026 GRPC_CREDENTIALS_OK);
nnoble0c475f02014-12-05 15:37:39 -08001027 composite_md_context_destroy(ctx);
1028}
1029
1030static void composite_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -08001031 const char *service_url,
nnoble0c475f02014-12-05 15:37:39 -08001032 grpc_credentials_metadata_cb cb,
1033 void *user_data) {
1034 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
1035 grpc_composite_credentials_metadata_context *ctx;
1036 if (!grpc_credentials_has_request_metadata(creds)) {
1037 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
1038 return;
1039 }
1040 ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context));
1041 memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context));
Julien Boeuff47a5cb2015-02-18 12:24:08 -08001042 ctx->service_url = gpr_strdup(service_url);
nnoble0c475f02014-12-05 15:37:39 -08001043 ctx->user_data = user_data;
1044 ctx->cb = cb;
1045 ctx->composite_creds = c;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001046 ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds);
nnoble0c475f02014-12-05 15:37:39 -08001047 while (ctx->creds_index < c->inner.num_creds) {
1048 grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++];
1049 if (grpc_credentials_has_request_metadata(inner_creds)) {
Julien Boeuff47a5cb2015-02-18 12:24:08 -08001050 grpc_credentials_get_request_metadata(inner_creds, service_url,
1051 composite_metadata_cb, ctx);
nnoble0c475f02014-12-05 15:37:39 -08001052 return;
1053 }
1054 }
1055 GPR_ASSERT(0); /* Should have exited before. */
1056}
1057
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001058static grpc_security_status composite_create_security_connector(
1059 grpc_credentials *creds, const char *target, const grpc_channel_args *args,
1060 grpc_credentials *request_metadata_creds,
1061 grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
1062 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
1063 if (c->connector_creds == NULL) {
1064 gpr_log(GPR_ERROR,
1065 "Cannot create security connector, missing connector credentials.");
1066 return GRPC_SECURITY_ERROR;
1067 }
1068 return grpc_credentials_create_security_connector(c->connector_creds, target,
1069 args, creds, sc, new_args);
1070}
1071
nnoble0c475f02014-12-05 15:37:39 -08001072static grpc_credentials_vtable composite_credentials_vtable = {
1073 composite_destroy, composite_has_request_metadata,
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001074 composite_has_request_metadata_only, composite_get_request_metadata,
1075 composite_create_security_connector};
nnoble0c475f02014-12-05 15:37:39 -08001076
1077static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
1078 grpc_credentials_array result;
1079 grpc_credentials *creds = *creds_addr;
1080 result.creds_array = creds_addr;
1081 result.num_creds = 1;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001082 if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) {
nnoble0c475f02014-12-05 15:37:39 -08001083 result = *grpc_composite_credentials_get_credentials(creds);
1084 }
1085 return result;
1086}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001087
1088grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
1089 grpc_credentials *creds2) {
nnoble0c475f02014-12-05 15:37:39 -08001090 size_t i;
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001091 size_t creds_array_byte_size;
nnoble0c475f02014-12-05 15:37:39 -08001092 grpc_credentials_array creds1_array;
1093 grpc_credentials_array creds2_array;
1094 grpc_composite_credentials *c;
1095 GPR_ASSERT(creds1 != NULL);
1096 GPR_ASSERT(creds2 != NULL);
1097 c = gpr_malloc(sizeof(grpc_composite_credentials));
1098 memset(c, 0, sizeof(grpc_composite_credentials));
1099 c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE;
1100 c->base.vtable = &composite_credentials_vtable;
1101 gpr_ref_init(&c->base.refcount, 1);
1102 creds1_array = get_creds_array(&creds1);
1103 creds2_array = get_creds_array(&creds2);
1104 c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001105 creds_array_byte_size = c->inner.num_creds * sizeof(grpc_credentials *);
1106 c->inner.creds_array = gpr_malloc(creds_array_byte_size);
1107 memset(c->inner.creds_array, 0, creds_array_byte_size);
nnoble0c475f02014-12-05 15:37:39 -08001108 for (i = 0; i < creds1_array.num_creds; i++) {
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001109 grpc_credentials *cur_creds = creds1_array.creds_array[i];
1110 if (!grpc_credentials_has_request_metadata_only(cur_creds)) {
1111 if (c->connector_creds == NULL) {
1112 c->connector_creds = cur_creds;
1113 } else {
1114 gpr_log(GPR_ERROR, "Cannot compose multiple connector credentials.");
1115 goto fail;
1116 }
1117 }
1118 c->inner.creds_array[i] = grpc_credentials_ref(cur_creds);
nnoble0c475f02014-12-05 15:37:39 -08001119 }
1120 for (i = 0; i < creds2_array.num_creds; i++) {
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001121 grpc_credentials *cur_creds = creds2_array.creds_array[i];
1122 if (!grpc_credentials_has_request_metadata_only(cur_creds)) {
1123 if (c->connector_creds == NULL) {
1124 c->connector_creds = cur_creds;
1125 } else {
1126 gpr_log(GPR_ERROR, "Cannot compose multiple connector credentials.");
1127 goto fail;
1128 }
1129 }
nnoble0c475f02014-12-05 15:37:39 -08001130 c->inner.creds_array[i + creds1_array.num_creds] =
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001131 grpc_credentials_ref(cur_creds);
nnoble0c475f02014-12-05 15:37:39 -08001132 }
1133 return &c->base;
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -07001134
1135fail:
1136 grpc_credentials_unref(&c->base);
1137 return NULL;
nnoble0c475f02014-12-05 15:37:39 -08001138}
1139
1140const grpc_credentials_array *grpc_composite_credentials_get_credentials(
1141 grpc_credentials *creds) {
1142 const grpc_composite_credentials *c =
1143 (const grpc_composite_credentials *)creds;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001144 GPR_ASSERT(strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0);
nnoble0c475f02014-12-05 15:37:39 -08001145 return &c->inner;
1146}
1147
jboeuf6ad120e2015-01-12 17:08:15 -08001148grpc_credentials *grpc_credentials_contains_type(
1149 grpc_credentials *creds, const char *type,
1150 grpc_credentials **composite_creds) {
1151 size_t i;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001152 if (strcmp(creds->type, type) == 0) {
jboeuf6ad120e2015-01-12 17:08:15 -08001153 if (composite_creds != NULL) *composite_creds = NULL;
1154 return creds;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001155 } else if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) {
jboeuf6ad120e2015-01-12 17:08:15 -08001156 const grpc_credentials_array *inner_creds_array =
1157 grpc_composite_credentials_get_credentials(creds);
1158 for (i = 0; i < inner_creds_array->num_creds; i++) {
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001159 if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) {
jboeuf6ad120e2015-01-12 17:08:15 -08001160 if (composite_creds != NULL) *composite_creds = creds;
1161 return inner_creds_array->creds_array[i];
1162 }
1163 }
1164 }
1165 return NULL;
1166}
1167
nnoble0c475f02014-12-05 15:37:39 -08001168/* -- IAM credentials. -- */
1169
1170typedef struct {
1171 grpc_credentials base;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001172 grpc_credentials_md_store *iam_md;
nnoble0c475f02014-12-05 15:37:39 -08001173} grpc_iam_credentials;
1174
1175static void iam_destroy(grpc_credentials *creds) {
1176 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001177 grpc_credentials_md_store_unref(c->iam_md);
nnoble0c475f02014-12-05 15:37:39 -08001178 gpr_free(c);
1179}
1180
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +01001181static int iam_has_request_metadata(const grpc_credentials *creds) {
1182 return 1;
1183}
nnoble0c475f02014-12-05 15:37:39 -08001184
1185static int iam_has_request_metadata_only(const grpc_credentials *creds) {
1186 return 1;
1187}
1188
1189static void iam_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -08001190 const char *service_url,
nnoble0c475f02014-12-05 15:37:39 -08001191 grpc_credentials_metadata_cb cb,
1192 void *user_data) {
1193 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001194 cb(user_data, c->iam_md->entries, c->iam_md->num_entries,
1195 GRPC_CREDENTIALS_OK);
Craig Tillerc4885ed2015-04-14 09:51:28 -07001196}
1197
nnoble0c475f02014-12-05 15:37:39 -08001198static grpc_credentials_vtable iam_vtable = {
1199 iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001200 iam_get_request_metadata, NULL};
nnoble0c475f02014-12-05 15:37:39 -08001201
1202grpc_credentials *grpc_iam_credentials_create(const char *token,
1203 const char *authority_selector) {
1204 grpc_iam_credentials *c;
1205 GPR_ASSERT(token != NULL);
1206 GPR_ASSERT(authority_selector != NULL);
1207 c = gpr_malloc(sizeof(grpc_iam_credentials));
1208 memset(c, 0, sizeof(grpc_iam_credentials));
1209 c->base.type = GRPC_CREDENTIALS_TYPE_IAM;
1210 c->base.vtable = &iam_vtable;
1211 gpr_ref_init(&c->base.refcount, 1);
Julien Boeuf75c9b6f2015-05-29 13:12:12 -07001212 c->iam_md = grpc_credentials_md_store_create(2);
1213 grpc_credentials_md_store_add_cstrings(
1214 c->iam_md, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
1215 grpc_credentials_md_store_add_cstrings(
1216 c->iam_md, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
nnoble0c475f02014-12-05 15:37:39 -08001217 return &c->base;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001218}