blob: 698e0991349822ee50d9b261054472e66ffe9ed9 [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
39#include "src/core/json/json.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080040#include "src/core/httpcli/httpcli.h"
ctiller18b49ab2014-12-09 14:39:16 -080041#include "src/core/iomgr/iomgr.h"
jboeuf1a809c02014-12-19 15:44:30 -080042#include "src/core/security/json_token.h"
Craig Tiller485d7762015-01-23 12:54:05 -080043#include "src/core/support/string.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080044#include <grpc/support/alloc.h>
45#include <grpc/support/log.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080046#include <grpc/support/sync.h>
47#include <grpc/support/time.h>
48
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080049/* -- Common. -- */
50
51typedef struct {
52 grpc_credentials *creds;
53 grpc_credentials_metadata_cb cb;
54 void *user_data;
55} grpc_credentials_metadata_request;
56
57static grpc_credentials_metadata_request *
58grpc_credentials_metadata_request_create(grpc_credentials *creds,
59 grpc_credentials_metadata_cb cb,
60 void *user_data) {
61 grpc_credentials_metadata_request *r =
62 gpr_malloc(sizeof(grpc_credentials_metadata_request));
63 r->creds = grpc_credentials_ref(creds);
64 r->cb = cb;
65 r->user_data = user_data;
66 return r;
67}
68
69static void grpc_credentials_metadata_request_destroy(
70 grpc_credentials_metadata_request *r) {
71 grpc_credentials_unref(r->creds);
72 gpr_free(r);
73}
74
75grpc_credentials *grpc_credentials_ref(grpc_credentials *creds) {
76 if (creds == NULL) return NULL;
77 gpr_ref(&creds->refcount);
78 return creds;
79}
80
81void grpc_credentials_unref(grpc_credentials *creds) {
82 if (creds == NULL) return;
83 if (gpr_unref(&creds->refcount)) creds->vtable->destroy(creds);
84}
85
86void grpc_credentials_release(grpc_credentials *creds) {
87 grpc_credentials_unref(creds);
88}
89
90int grpc_credentials_has_request_metadata(grpc_credentials *creds) {
91 if (creds == NULL) return 0;
92 return creds->vtable->has_request_metadata(creds);
93}
94
95int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) {
96 if (creds == NULL) return 0;
97 return creds->vtable->has_request_metadata_only(creds);
98}
99
100void grpc_credentials_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800101 const char *service_url,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800102 grpc_credentials_metadata_cb cb,
103 void *user_data) {
nnoble0c475f02014-12-05 15:37:39 -0800104 if (creds == NULL || !grpc_credentials_has_request_metadata(creds) ||
105 creds->vtable->get_request_metadata == NULL) {
106 if (cb != NULL) {
107 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
108 }
109 return;
110 }
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800111 creds->vtable->get_request_metadata(creds, service_url, cb, user_data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800112}
113
114void grpc_server_credentials_release(grpc_server_credentials *creds) {
115 if (creds == NULL) return;
116 creds->vtable->destroy(creds);
117}
118
119/* -- Ssl credentials. -- */
120
121typedef struct {
122 grpc_credentials base;
123 grpc_ssl_config config;
124} grpc_ssl_credentials;
125
126typedef struct {
127 grpc_server_credentials base;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800128 grpc_ssl_server_config config;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800129} grpc_ssl_server_credentials;
130
131static void ssl_destroy(grpc_credentials *creds) {
132 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
133 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
134 if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
135 if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
136 gpr_free(creds);
137}
138
139static void ssl_server_destroy(grpc_server_credentials *creds) {
140 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800141 size_t i;
142 for (i = 0; i < c->config.num_key_cert_pairs; i++) {
143 if (c->config.pem_private_keys[i] != NULL) {
144 gpr_free(c->config.pem_private_keys[i]);
145 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800146 if (c->config.pem_cert_chains[i] != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800147 gpr_free(c->config.pem_cert_chains[i]);
148 }
149 }
150 if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys);
151 if (c->config.pem_private_keys_sizes != NULL) {
152 gpr_free(c->config.pem_private_keys_sizes);
153 }
154 if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains);
155 if (c->config.pem_cert_chains_sizes != NULL) {
156 gpr_free(c->config.pem_cert_chains_sizes);
157 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800158 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800159 gpr_free(creds);
160}
161
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100162static int ssl_has_request_metadata(const grpc_credentials *creds) {
163 return 0;
164}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800165
166static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
167 return 0;
168}
169
170static grpc_credentials_vtable ssl_vtable = {
171 ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL};
172
173static grpc_server_credentials_vtable ssl_server_vtable = {ssl_server_destroy};
174
175const grpc_ssl_config *grpc_ssl_credentials_get_config(
176 const grpc_credentials *creds) {
177 if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
178 return NULL;
179 } else {
180 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
181 return &c->config;
182 }
183}
184
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800185const grpc_ssl_server_config *grpc_ssl_server_credentials_get_config(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800186 const grpc_server_credentials *creds) {
187 if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
188 return NULL;
189 } else {
190 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
191 return &c->config;
192 }
193}
194
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800195static void ssl_copy_key_material(const char *input, unsigned char **output,
196 size_t *output_size) {
197 *output_size = strlen(input);
198 *output = gpr_malloc(*output_size);
199 memcpy(*output, input, *output_size);
200}
201
202static void ssl_build_config(const char *pem_root_certs,
203 grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800204 grpc_ssl_config *config) {
Julien Boeuf026a4172015-02-02 18:36:37 -0800205 if (pem_root_certs != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800206 ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
207 &config->pem_root_certs_size);
208 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800209 if (pem_key_cert_pair != NULL) {
210 GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
211 GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
212 ssl_copy_key_material(pem_key_cert_pair->private_key,
213 &config->pem_private_key,
214 &config->pem_private_key_size);
215 ssl_copy_key_material(pem_key_cert_pair->cert_chain,
216 &config->pem_cert_chain,
217 &config->pem_cert_chain_size);
218 }
219}
220
221static void ssl_build_server_config(
222 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
223 size_t num_key_cert_pairs, grpc_ssl_server_config *config) {
224 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800225 if (pem_root_certs != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800226 ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
227 &config->pem_root_certs_size);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800228 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800229 if (num_key_cert_pairs > 0) {
230 GPR_ASSERT(pem_key_cert_pairs != NULL);
231 config->pem_private_keys =
232 gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
233 config->pem_cert_chains =
234 gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
235 config->pem_private_keys_sizes =
236 gpr_malloc(num_key_cert_pairs * sizeof(size_t));
237 config->pem_cert_chains_sizes =
238 gpr_malloc(num_key_cert_pairs * sizeof(size_t));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800239 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800240 config->num_key_cert_pairs = num_key_cert_pairs;
241 for (i = 0; i < num_key_cert_pairs; i++) {
242 GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
243 GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
244 ssl_copy_key_material(pem_key_cert_pairs[i].private_key,
245 &config->pem_private_keys[i],
246 &config->pem_private_keys_sizes[i]);
247 ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain,
248 &config->pem_cert_chains[i],
249 &config->pem_cert_chains_sizes[i]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800250 }
251}
252
253grpc_credentials *grpc_ssl_credentials_create(
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800254 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800255 grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
256 memset(c, 0, sizeof(grpc_ssl_credentials));
257 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
258 c->base.vtable = &ssl_vtable;
259 gpr_ref_init(&c->base.refcount, 1);
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800260 ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800261 return &c->base;
262}
263
264grpc_server_credentials *grpc_ssl_server_credentials_create(
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800265 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
266 size_t num_key_cert_pairs) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800267 grpc_ssl_server_credentials *c =
268 gpr_malloc(sizeof(grpc_ssl_server_credentials));
269 memset(c, 0, sizeof(grpc_ssl_server_credentials));
270 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
271 c->base.vtable = &ssl_server_vtable;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800272 ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
273 num_key_cert_pairs, &c->config);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800274 return &c->base;
275}
276
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800277/* -- Jwt credentials -- */
278
279typedef struct {
280 grpc_credentials base;
281 grpc_mdctx *md_ctx;
282
283 /* Have a simple cache for now with just 1 entry. We could have a map based on
284 the service_url for a more sophisticated one. */
285 gpr_mu cache_mu;
286 struct {
287 grpc_mdelem *jwt_md;
288 char *service_url;
289 gpr_timespec jwt_expiration;
290 } cached;
291
292 grpc_auth_json_key key;
293 gpr_timespec jwt_lifetime;
294} grpc_jwt_credentials;
295
296static void jwt_reset_cache(grpc_jwt_credentials *c) {
297 if (c->cached.jwt_md != NULL) {
298 grpc_mdelem_unref(c->cached.jwt_md);
299 c->cached.jwt_md = NULL;
300 }
301 if (c->cached.service_url != NULL) {
302 gpr_free(c->cached.service_url);
303 c->cached.service_url = NULL;
304 }
305 c->cached.jwt_expiration = gpr_inf_past;
306}
307
308static void jwt_destroy(grpc_credentials *creds) {
309 grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds;
310 grpc_auth_json_key_destruct(&c->key);
311 jwt_reset_cache(c);
312 gpr_mu_destroy(&c->cache_mu);
313 grpc_mdctx_unref(c->md_ctx);
314 gpr_free(c);
315}
316
317static int jwt_has_request_metadata(const grpc_credentials *creds) { return 1; }
318
319static int jwt_has_request_metadata_only(const grpc_credentials *creds) {
320 return 1;
321}
322
323
324static void jwt_get_request_metadata(grpc_credentials *creds,
325 const char *service_url,
326 grpc_credentials_metadata_cb cb,
327 void *user_data) {
328 grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds;
329 gpr_timespec refresh_threshold = {GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
330 0};
331
332 /* See if we can return a cached jwt. */
333 grpc_mdelem *jwt_md = NULL;
334 {
335 gpr_mu_lock(&c->cache_mu);
336 if (c->cached.service_url != NULL &&
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -0800337 strcmp(c->cached.service_url, service_url) == 0 &&
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800338 c->cached.jwt_md != NULL &&
339 (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now()),
340 refresh_threshold) > 0)) {
341 jwt_md = grpc_mdelem_ref(c->cached.jwt_md);
342 }
343 gpr_mu_unlock(&c->cache_mu);
344 }
345
346 if (jwt_md == NULL) {
347 char *jwt = NULL;
348 /* Generate a new jwt. */
349 gpr_mu_lock(&c->cache_mu);
350 jwt_reset_cache(c);
351 jwt = grpc_jwt_encode_and_sign(&c->key, service_url, c->jwt_lifetime, NULL);
352 if (jwt != NULL) {
353 char *md_value;
354 gpr_asprintf(&md_value, "Bearer %s", jwt);
355 gpr_free(jwt);
356 c->cached.jwt_expiration = gpr_time_add(gpr_now(), c->jwt_lifetime);
357 c->cached.service_url = gpr_strdup(service_url);
358 c->cached.jwt_md = grpc_mdelem_from_strings(
359 c->md_ctx, GRPC_AUTHORIZATION_METADATA_KEY, md_value);
360 gpr_free(md_value);
361 jwt_md = grpc_mdelem_ref(c->cached.jwt_md);
362 }
363 gpr_mu_unlock(&c->cache_mu);
364 }
365
366 if (jwt_md != NULL) {
367 cb(user_data, &jwt_md, 1, GRPC_CREDENTIALS_OK);
368 grpc_mdelem_unref(jwt_md);
369 } else {
370 cb(user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
371 }
372}
373
374static grpc_credentials_vtable jwt_vtable = {
375 jwt_destroy, jwt_has_request_metadata, jwt_has_request_metadata_only,
376 jwt_get_request_metadata};
377
378grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
379 gpr_timespec token_lifetime) {
380 grpc_jwt_credentials *c;
381 grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
382 if (!grpc_auth_json_key_is_valid(&key)) {
383 gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
384 return NULL;
385 }
386 c = gpr_malloc(sizeof(grpc_jwt_credentials));
387 memset(c, 0, sizeof(grpc_jwt_credentials));
388 c->base.type = GRPC_CREDENTIALS_TYPE_JWT;
389 gpr_ref_init(&c->base.refcount, 1);
390 c->base.vtable = &jwt_vtable;
391 c->md_ctx = grpc_mdctx_create();
392 c->key = key;
393 c->jwt_lifetime = token_lifetime;
394 gpr_mu_init(&c->cache_mu);
395 jwt_reset_cache(c);
396 return &c->base;
397}
398
jboeuf1a809c02014-12-19 15:44:30 -0800399/* -- Oauth2TokenFetcher credentials -- */
400
401/* This object is a base for credentials that need to acquire an oauth2 token
402 from an http service. */
403
404typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req,
405 grpc_httpcli_response_cb response_cb,
406 gpr_timespec deadline);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800407
408typedef struct {
409 grpc_credentials base;
410 gpr_mu mu;
411 grpc_mdctx *md_ctx;
412 grpc_mdelem *access_token_md;
413 gpr_timespec token_expiration;
jboeuf1a809c02014-12-19 15:44:30 -0800414 grpc_fetch_oauth2_func fetch_func;
415} grpc_oauth2_token_fetcher_credentials;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800416
jboeuf1a809c02014-12-19 15:44:30 -0800417static void oauth2_token_fetcher_destroy(grpc_credentials *creds) {
418 grpc_oauth2_token_fetcher_credentials *c =
419 (grpc_oauth2_token_fetcher_credentials *)creds;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800420 if (c->access_token_md != NULL) {
421 grpc_mdelem_unref(c->access_token_md);
422 }
423 gpr_mu_destroy(&c->mu);
Craig Tiller9be83ee2015-02-18 14:16:15 -0800424 grpc_mdctx_unref(c->md_ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800425 gpr_free(c);
426}
427
jboeuf1a809c02014-12-19 15:44:30 -0800428static int oauth2_token_fetcher_has_request_metadata(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800429 const grpc_credentials *creds) {
430 return 1;
431}
432
jboeuf1a809c02014-12-19 15:44:30 -0800433static int oauth2_token_fetcher_has_request_metadata_only(
434 const grpc_credentials *creds) {
435 return 1;
436}
437
438grpc_credentials_status
439grpc_oauth2_token_fetcher_credentials_parse_server_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800440 const grpc_httpcli_response *response, grpc_mdctx *ctx,
441 grpc_mdelem **token_elem, gpr_timespec *token_lifetime) {
442 char *null_terminated_body = NULL;
443 char *new_access_token = NULL;
444 grpc_credentials_status status = GRPC_CREDENTIALS_OK;
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800445 grpc_json *json = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800446
Julien Boeuf90bd7c42015-02-18 18:51:16 -0800447 if (response == NULL) {
448 gpr_log(GPR_ERROR, "Received NULL response.");
449 status = GRPC_CREDENTIALS_ERROR;
450 goto end;
451 }
452
jboeuf1a809c02014-12-19 15:44:30 -0800453 if (response->body_length > 0) {
454 null_terminated_body = gpr_malloc(response->body_length + 1);
455 null_terminated_body[response->body_length] = '\0';
456 memcpy(null_terminated_body, response->body, response->body_length);
457 }
458
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800459 if (response->status != 200) {
jboeuf1a809c02014-12-19 15:44:30 -0800460 gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
461 response->status,
462 null_terminated_body != NULL ? null_terminated_body : "");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800463 status = GRPC_CREDENTIALS_ERROR;
464 goto end;
465 } else {
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800466 grpc_json *access_token = NULL;
467 grpc_json *token_type = NULL;
468 grpc_json *expires_in = NULL;
469 grpc_json *ptr;
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800470 json = grpc_json_parse_string(null_terminated_body);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800471 if (json == NULL) {
472 gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
473 status = GRPC_CREDENTIALS_ERROR;
474 goto end;
475 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800476 if (json->type != GRPC_JSON_OBJECT) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800477 gpr_log(GPR_ERROR, "Response should be a JSON object");
478 status = GRPC_CREDENTIALS_ERROR;
479 goto end;
480 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800481 for (ptr = json->child; ptr; ptr = ptr->next) {
482 if (strcmp(ptr->key, "access_token") == 0) {
483 access_token = ptr;
484 } else if (strcmp(ptr->key, "token_type") == 0) {
485 token_type = ptr;
486 } else if (strcmp(ptr->key, "expires_in") == 0) {
487 expires_in = ptr;
488 }
489 }
490 if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800491 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
492 status = GRPC_CREDENTIALS_ERROR;
493 goto end;
494 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800495 if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800496 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
497 status = GRPC_CREDENTIALS_ERROR;
498 goto end;
499 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800500 if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800501 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
502 status = GRPC_CREDENTIALS_ERROR;
503 goto end;
504 }
Nicolas Noblefee065c2015-01-26 11:41:12 -0800505 gpr_asprintf(&new_access_token, "%s %s", token_type->value,
506 access_token->value);
Nicolas Noblee04455a2015-01-26 17:01:29 -0800507 token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800508 token_lifetime->tv_nsec = 0;
509 if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
510 *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
511 new_access_token);
512 status = GRPC_CREDENTIALS_OK;
513 }
514
515end:
516 if (status != GRPC_CREDENTIALS_OK && (*token_elem != NULL)) {
517 grpc_mdelem_unref(*token_elem);
518 *token_elem = NULL;
519 }
520 if (null_terminated_body != NULL) gpr_free(null_terminated_body);
521 if (new_access_token != NULL) gpr_free(new_access_token);
Nicolas Noble8c2be9b2015-01-27 14:21:18 -0800522 if (json != NULL) grpc_json_destroy(json);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800523 return status;
524}
525
jboeuf1a809c02014-12-19 15:44:30 -0800526static void on_oauth2_token_fetcher_http_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800527 void *user_data, const grpc_httpcli_response *response) {
528 grpc_credentials_metadata_request *r =
529 (grpc_credentials_metadata_request *)user_data;
jboeuf1a809c02014-12-19 15:44:30 -0800530 grpc_oauth2_token_fetcher_credentials *c =
531 (grpc_oauth2_token_fetcher_credentials *)r->creds;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800532 gpr_timespec token_lifetime;
533 grpc_credentials_status status;
534
535 gpr_mu_lock(&c->mu);
jboeuf1a809c02014-12-19 15:44:30 -0800536 status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800537 response, c->md_ctx, &c->access_token_md, &token_lifetime);
538 if (status == GRPC_CREDENTIALS_OK) {
539 c->token_expiration = gpr_time_add(gpr_now(), token_lifetime);
540 r->cb(r->user_data, &c->access_token_md, 1, status);
541 } else {
542 c->token_expiration = gpr_inf_past;
543 r->cb(r->user_data, NULL, 0, status);
544 }
545 gpr_mu_unlock(&c->mu);
546 grpc_credentials_metadata_request_destroy(r);
547}
548
jboeuf1a809c02014-12-19 15:44:30 -0800549static void oauth2_token_fetcher_get_request_metadata(
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800550 grpc_credentials *creds, const char *service_url,
551 grpc_credentials_metadata_cb cb, void *user_data) {
jboeuf1a809c02014-12-19 15:44:30 -0800552 grpc_oauth2_token_fetcher_credentials *c =
553 (grpc_oauth2_token_fetcher_credentials *)creds;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800554 gpr_timespec refresh_threshold = {GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS,
jboeuf1a809c02014-12-19 15:44:30 -0800555 0};
556 grpc_mdelem *cached_access_token_md = NULL;
557 {
558 gpr_mu_lock(&c->mu);
559 if (c->access_token_md != NULL &&
560 (gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()),
561 refresh_threshold) > 0)) {
562 cached_access_token_md = grpc_mdelem_ref(c->access_token_md);
563 }
564 gpr_mu_unlock(&c->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800565 }
jboeuf1a809c02014-12-19 15:44:30 -0800566 if (cached_access_token_md != NULL) {
567 cb(user_data, &cached_access_token_md, 1, GRPC_CREDENTIALS_OK);
568 grpc_mdelem_unref(cached_access_token_md);
569 } else {
570 c->fetch_func(
571 grpc_credentials_metadata_request_create(creds, cb, user_data),
572 on_oauth2_token_fetcher_http_response,
573 gpr_time_add(gpr_now(), refresh_threshold));
574 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800575}
576
jboeuf1a809c02014-12-19 15:44:30 -0800577static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
578 grpc_fetch_oauth2_func fetch_func) {
579 memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800580 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800581 gpr_ref_init(&c->base.refcount, 1);
582 gpr_mu_init(&c->mu);
583 c->md_ctx = grpc_mdctx_create();
584 c->token_expiration = gpr_inf_past;
jboeuf1a809c02014-12-19 15:44:30 -0800585 c->fetch_func = fetch_func;
586}
587
588/* -- ComputeEngine credentials. -- */
589
590static grpc_credentials_vtable compute_engine_vtable = {
591 oauth2_token_fetcher_destroy, oauth2_token_fetcher_has_request_metadata,
592 oauth2_token_fetcher_has_request_metadata_only,
593 oauth2_token_fetcher_get_request_metadata};
594
595static void compute_engine_fetch_oauth2(
596 grpc_credentials_metadata_request *metadata_req,
597 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
598 grpc_httpcli_header header = {"Metadata-Flavor", "Google"};
599 grpc_httpcli_request request;
600 memset(&request, 0, sizeof(grpc_httpcli_request));
601 request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
602 request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
603 request.hdr_count = 1;
604 request.hdrs = &header;
605 grpc_httpcli_get(&request, deadline, response_cb, metadata_req);
606}
607
608grpc_credentials *grpc_compute_engine_credentials_create(void) {
609 grpc_oauth2_token_fetcher_credentials *c =
610 gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
611 init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
612 c->base.vtable = &compute_engine_vtable;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800613 return &c->base;
614}
615
jboeuf1a809c02014-12-19 15:44:30 -0800616/* -- ServiceAccount credentials. -- */
617
618typedef struct {
619 grpc_oauth2_token_fetcher_credentials base;
620 grpc_auth_json_key key;
621 char *scope;
622 gpr_timespec token_lifetime;
623} grpc_service_account_credentials;
624
625static void service_account_destroy(grpc_credentials *creds) {
626 grpc_service_account_credentials *c =
627 (grpc_service_account_credentials *)creds;
628 if (c->scope != NULL) gpr_free(c->scope);
629 grpc_auth_json_key_destruct(&c->key);
630 oauth2_token_fetcher_destroy(&c->base.base);
631}
632
633static grpc_credentials_vtable service_account_vtable = {
634 service_account_destroy, oauth2_token_fetcher_has_request_metadata,
635 oauth2_token_fetcher_has_request_metadata_only,
636 oauth2_token_fetcher_get_request_metadata};
637
638static void service_account_fetch_oauth2(
639 grpc_credentials_metadata_request *metadata_req,
640 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
641 grpc_service_account_credentials *c =
642 (grpc_service_account_credentials *)metadata_req->creds;
643 grpc_httpcli_header header = {"Content-Type",
644 "application/x-www-form-urlencoded"};
645 grpc_httpcli_request request;
646 char *body = NULL;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800647 char *jwt = grpc_jwt_encode_and_sign(&c->key, GRPC_JWT_OAUTH2_AUDIENCE,
648 c->token_lifetime, c->scope);
jboeuf1a809c02014-12-19 15:44:30 -0800649 if (jwt == NULL) {
650 grpc_httpcli_response response;
651 memset(&response, 0, sizeof(grpc_httpcli_response));
652 response.status = 400; /* Invalid request. */
653 gpr_log(GPR_ERROR, "Could not create signed jwt.");
654 /* Do not even send the request, just call the response callback. */
655 response_cb(metadata_req, &response);
656 return;
657 }
Craig Tillerbe6a3552015-01-23 13:04:45 -0800658 gpr_asprintf(&body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt);
jboeuf1a809c02014-12-19 15:44:30 -0800659 memset(&request, 0, sizeof(grpc_httpcli_request));
Julien Boeuf9835cf02015-03-09 16:56:44 -0700660 request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
661 request.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
jboeuf1a809c02014-12-19 15:44:30 -0800662 request.hdr_count = 1;
663 request.hdrs = &header;
664 request.use_ssl = 1;
665 grpc_httpcli_post(&request, body, strlen(body), deadline, response_cb,
666 metadata_req);
667 gpr_free(body);
668 gpr_free(jwt);
669}
670
671grpc_credentials *grpc_service_account_credentials_create(
672 const char *json_key, const char *scope, gpr_timespec token_lifetime) {
673 grpc_service_account_credentials *c;
674 grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
675
676 if (scope == NULL || (strlen(scope) == 0) ||
677 !grpc_auth_json_key_is_valid(&key)) {
678 gpr_log(GPR_ERROR,
679 "Invalid input for service account credentials creation");
680 return NULL;
681 }
682 c = gpr_malloc(sizeof(grpc_service_account_credentials));
683 memset(c, 0, sizeof(grpc_service_account_credentials));
684 init_oauth2_token_fetcher(&c->base, service_account_fetch_oauth2);
685 c->base.base.vtable = &service_account_vtable;
686 c->scope = gpr_strdup(scope);
687 c->key = key;
688 c->token_lifetime = token_lifetime;
689 return &c->base.base;
690}
691
Julien Boeuf9835cf02015-03-09 16:56:44 -0700692/* -- RefreshToken credentials. -- */
693
694typedef struct {
695 grpc_oauth2_token_fetcher_credentials base;
696 grpc_auth_refresh_token refresh_token;
697} grpc_refresh_token_credentials;
698
699static void refresh_token_destroy(grpc_credentials *creds) {
700 grpc_refresh_token_credentials *c =
701 (grpc_refresh_token_credentials *)creds;
702 grpc_auth_refresh_token_destruct(&c->refresh_token);
703 oauth2_token_fetcher_destroy(&c->base.base);
704}
705
706static grpc_credentials_vtable refresh_token_vtable = {
707 refresh_token_destroy, oauth2_token_fetcher_has_request_metadata,
708 oauth2_token_fetcher_has_request_metadata_only,
709 oauth2_token_fetcher_get_request_metadata};
710
711static void refresh_token_fetch_oauth2(
712 grpc_credentials_metadata_request *metadata_req,
713 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
714 grpc_refresh_token_credentials *c =
715 (grpc_refresh_token_credentials *)metadata_req->creds;
716 grpc_httpcli_header header = {"Content-Type",
717 "application/x-www-form-urlencoded"};
718 grpc_httpcli_request request;
719 char *body = NULL;
720 gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
721 c->refresh_token.client_id, c->refresh_token.client_secret,
722 c->refresh_token.refresh_token);
723 memset(&request, 0, sizeof(grpc_httpcli_request));
724 request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
725 request.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
726 request.hdr_count = 1;
727 request.hdrs = &header;
728 request.use_ssl = 1;
729 grpc_httpcli_post(&request, body, strlen(body), deadline, response_cb,
730 metadata_req);
731 gpr_free(body);
732}
733
734grpc_credentials *grpc_refresh_token_credentials_create(
735 const char *json_refresh_token) {
736 grpc_refresh_token_credentials *c;
737 grpc_auth_refresh_token refresh_token =
738 grpc_auth_refresh_token_create_from_string(json_refresh_token);
739
740 if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
741 gpr_log(GPR_ERROR,
742 "Invalid input for refresh token credentials creation");
743 return NULL;
744 }
745 c = gpr_malloc(sizeof(grpc_refresh_token_credentials));
746 memset(c, 0, sizeof(grpc_refresh_token_credentials));
747 init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2);
748 c->base.base.vtable = &refresh_token_vtable;
749 c->refresh_token = refresh_token;
750 return &c->base.base;
751}
752
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800753/* -- Fake Oauth2 credentials. -- */
754
755typedef struct {
756 grpc_credentials base;
757 grpc_mdctx *md_ctx;
758 grpc_mdelem *access_token_md;
759 int is_async;
760} grpc_fake_oauth2_credentials;
761
762static void fake_oauth2_destroy(grpc_credentials *creds) {
763 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
764 if (c->access_token_md != NULL) {
765 grpc_mdelem_unref(c->access_token_md);
766 }
Craig Tiller9be83ee2015-02-18 14:16:15 -0800767 grpc_mdctx_unref(c->md_ctx);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800768 gpr_free(c);
769}
770
771static int fake_oauth2_has_request_metadata(const grpc_credentials *creds) {
772 return 1;
773}
774
775static int fake_oauth2_has_request_metadata_only(
776 const grpc_credentials *creds) {
777 return 1;
778}
779
ctiller58393c22015-01-07 14:03:30 -0800780void on_simulated_token_fetch_done(void *user_data, int success) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800781 grpc_credentials_metadata_request *r =
782 (grpc_credentials_metadata_request *)user_data;
783 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)r->creds;
ctiller58393c22015-01-07 14:03:30 -0800784 GPR_ASSERT(success);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800785 r->cb(r->user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
786 grpc_credentials_metadata_request_destroy(r);
787}
788
789static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800790 const char *service_url,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800791 grpc_credentials_metadata_cb cb,
792 void *user_data) {
793 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
794
795 if (c->is_async) {
ctiller18b49ab2014-12-09 14:39:16 -0800796 grpc_iomgr_add_callback(
797 on_simulated_token_fetch_done,
798 grpc_credentials_metadata_request_create(creds, cb, user_data));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800799 } else {
800 cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
801 }
802}
803
804static grpc_credentials_vtable fake_oauth2_vtable = {
805 fake_oauth2_destroy, fake_oauth2_has_request_metadata,
806 fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata};
807
808grpc_credentials *grpc_fake_oauth2_credentials_create(
809 const char *token_md_value, int is_async) {
810 grpc_fake_oauth2_credentials *c =
811 gpr_malloc(sizeof(grpc_fake_oauth2_credentials));
812 memset(c, 0, sizeof(grpc_fake_oauth2_credentials));
813 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
814 c->base.vtable = &fake_oauth2_vtable;
815 gpr_ref_init(&c->base.refcount, 1);
816 c->md_ctx = grpc_mdctx_create();
817 c->access_token_md = grpc_mdelem_from_strings(
818 c->md_ctx, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
819 c->is_async = is_async;
820 return &c->base;
821}
822
823/* -- Fake transport security credentials. -- */
824
825static void fake_transport_security_credentials_destroy(
826 grpc_credentials *creds) {
827 gpr_free(creds);
828}
829
830static void fake_transport_security_server_credentials_destroy(
831 grpc_server_credentials *creds) {
832 gpr_free(creds);
833}
834
835static int fake_transport_security_has_request_metadata(
836 const grpc_credentials *creds) {
837 return 0;
838}
839
840static int fake_transport_security_has_request_metadata_only(
841 const grpc_credentials *creds) {
842 return 0;
843}
844
845static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
846 fake_transport_security_credentials_destroy,
847 fake_transport_security_has_request_metadata,
848 fake_transport_security_has_request_metadata_only, NULL};
849
850static grpc_server_credentials_vtable
851 fake_transport_security_server_credentials_vtable = {
852 fake_transport_security_server_credentials_destroy};
853
854grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
855 grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
856 memset(c, 0, sizeof(grpc_credentials));
857 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
858 c->vtable = &fake_transport_security_credentials_vtable;
859 gpr_ref_init(&c->refcount, 1);
860 return c;
861}
862
Craig Tiller3eef2c42015-01-15 11:37:54 -0800863grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
864 void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800865 grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
866 memset(c, 0, sizeof(grpc_server_credentials));
867 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
868 c->vtable = &fake_transport_security_server_credentials_vtable;
869 return c;
870}
871
nnoble0c475f02014-12-05 15:37:39 -0800872/* -- Composite credentials. -- */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800873
nnoble0c475f02014-12-05 15:37:39 -0800874typedef struct {
875 grpc_credentials base;
876 grpc_credentials_array inner;
877} grpc_composite_credentials;
878
879typedef struct {
880 grpc_composite_credentials *composite_creds;
881 size_t creds_index;
882 grpc_mdelem **md_elems;
883 size_t num_md;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800884 char *service_url;
nnoble0c475f02014-12-05 15:37:39 -0800885 void *user_data;
886 grpc_credentials_metadata_cb cb;
887} grpc_composite_credentials_metadata_context;
888
889static void composite_destroy(grpc_credentials *creds) {
890 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
891 size_t i;
892 for (i = 0; i < c->inner.num_creds; i++) {
893 grpc_credentials_unref(c->inner.creds_array[i]);
894 }
895 gpr_free(c->inner.creds_array);
896 gpr_free(creds);
897}
898
899static int composite_has_request_metadata(const grpc_credentials *creds) {
900 const grpc_composite_credentials *c =
901 (const grpc_composite_credentials *)creds;
902 size_t i;
903 for (i = 0; i < c->inner.num_creds; i++) {
904 if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) {
905 return 1;
906 }
907 }
908 return 0;
909}
910
911static int composite_has_request_metadata_only(const grpc_credentials *creds) {
912 const grpc_composite_credentials *c =
913 (const grpc_composite_credentials *)creds;
914 size_t i;
915 for (i = 0; i < c->inner.num_creds; i++) {
916 if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) {
917 return 0;
918 }
919 }
920 return 1;
921}
922
923static void composite_md_context_destroy(
924 grpc_composite_credentials_metadata_context *ctx) {
925 size_t i;
926 for (i = 0; i < ctx->num_md; i++) {
927 grpc_mdelem_unref(ctx->md_elems[i]);
928 }
929 gpr_free(ctx->md_elems);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800930 if (ctx->service_url != NULL) gpr_free(ctx->service_url);
nnoble0c475f02014-12-05 15:37:39 -0800931 gpr_free(ctx);
932}
933
934static void composite_metadata_cb(void *user_data, grpc_mdelem **md_elems,
935 size_t num_md,
936 grpc_credentials_status status) {
937 grpc_composite_credentials_metadata_context *ctx =
938 (grpc_composite_credentials_metadata_context *)user_data;
939 size_t i;
940 if (status != GRPC_CREDENTIALS_OK) {
941 ctx->cb(ctx->user_data, NULL, 0, status);
942 return;
943 }
944
945 /* Copy the metadata in the context. */
946 if (num_md > 0) {
947 ctx->md_elems = gpr_realloc(ctx->md_elems,
948 (ctx->num_md + num_md) * sizeof(grpc_mdelem *));
949 for (i = 0; i < num_md; i++) {
950 ctx->md_elems[i + ctx->num_md] = grpc_mdelem_ref(md_elems[i]);
951 }
952 ctx->num_md += num_md;
953 }
954
955 /* See if we need to get some more metadata. */
956 while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
957 grpc_credentials *inner_creds =
958 ctx->composite_creds->inner.creds_array[ctx->creds_index++];
959 if (grpc_credentials_has_request_metadata(inner_creds)) {
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800960 grpc_credentials_get_request_metadata(inner_creds, ctx->service_url,
961 composite_metadata_cb, ctx);
nnoble0c475f02014-12-05 15:37:39 -0800962 return;
963 }
964 }
965
966 /* We're done!. */
967 ctx->cb(ctx->user_data, ctx->md_elems, ctx->num_md, GRPC_CREDENTIALS_OK);
968 composite_md_context_destroy(ctx);
969}
970
971static void composite_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800972 const char *service_url,
nnoble0c475f02014-12-05 15:37:39 -0800973 grpc_credentials_metadata_cb cb,
974 void *user_data) {
975 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
976 grpc_composite_credentials_metadata_context *ctx;
977 if (!grpc_credentials_has_request_metadata(creds)) {
978 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
979 return;
980 }
981 ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context));
982 memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context));
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800983 ctx->service_url = gpr_strdup(service_url);
nnoble0c475f02014-12-05 15:37:39 -0800984 ctx->user_data = user_data;
985 ctx->cb = cb;
986 ctx->composite_creds = c;
987 while (ctx->creds_index < c->inner.num_creds) {
988 grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++];
989 if (grpc_credentials_has_request_metadata(inner_creds)) {
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800990 grpc_credentials_get_request_metadata(inner_creds, service_url,
991 composite_metadata_cb, ctx);
nnoble0c475f02014-12-05 15:37:39 -0800992 return;
993 }
994 }
995 GPR_ASSERT(0); /* Should have exited before. */
996}
997
998static grpc_credentials_vtable composite_credentials_vtable = {
999 composite_destroy, composite_has_request_metadata,
1000 composite_has_request_metadata_only, composite_get_request_metadata};
1001
1002static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
1003 grpc_credentials_array result;
1004 grpc_credentials *creds = *creds_addr;
1005 result.creds_array = creds_addr;
1006 result.num_creds = 1;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001007 if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) {
nnoble0c475f02014-12-05 15:37:39 -08001008 result = *grpc_composite_credentials_get_credentials(creds);
1009 }
1010 return result;
1011}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001012
1013grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
1014 grpc_credentials *creds2) {
nnoble0c475f02014-12-05 15:37:39 -08001015 size_t i;
1016 grpc_credentials_array creds1_array;
1017 grpc_credentials_array creds2_array;
1018 grpc_composite_credentials *c;
1019 GPR_ASSERT(creds1 != NULL);
1020 GPR_ASSERT(creds2 != NULL);
1021 c = gpr_malloc(sizeof(grpc_composite_credentials));
1022 memset(c, 0, sizeof(grpc_composite_credentials));
1023 c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE;
1024 c->base.vtable = &composite_credentials_vtable;
1025 gpr_ref_init(&c->base.refcount, 1);
1026 creds1_array = get_creds_array(&creds1);
1027 creds2_array = get_creds_array(&creds2);
1028 c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
1029 c->inner.creds_array =
1030 gpr_malloc(c->inner.num_creds * sizeof(grpc_credentials *));
1031 for (i = 0; i < creds1_array.num_creds; i++) {
1032 c->inner.creds_array[i] = grpc_credentials_ref(creds1_array.creds_array[i]);
1033 }
1034 for (i = 0; i < creds2_array.num_creds; i++) {
1035 c->inner.creds_array[i + creds1_array.num_creds] =
1036 grpc_credentials_ref(creds2_array.creds_array[i]);
1037 }
1038 return &c->base;
1039}
1040
1041const grpc_credentials_array *grpc_composite_credentials_get_credentials(
1042 grpc_credentials *creds) {
1043 const grpc_composite_credentials *c =
1044 (const grpc_composite_credentials *)creds;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001045 GPR_ASSERT(strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0);
nnoble0c475f02014-12-05 15:37:39 -08001046 return &c->inner;
1047}
1048
jboeuf6ad120e2015-01-12 17:08:15 -08001049grpc_credentials *grpc_credentials_contains_type(
1050 grpc_credentials *creds, const char *type,
1051 grpc_credentials **composite_creds) {
1052 size_t i;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001053 if (strcmp(creds->type, type) == 0) {
jboeuf6ad120e2015-01-12 17:08:15 -08001054 if (composite_creds != NULL) *composite_creds = NULL;
1055 return creds;
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001056 } else if (strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE) == 0) {
jboeuf6ad120e2015-01-12 17:08:15 -08001057 const grpc_credentials_array *inner_creds_array =
1058 grpc_composite_credentials_get_credentials(creds);
1059 for (i = 0; i < inner_creds_array->num_creds; i++) {
Ronnie Sahlberg2ad8d212015-03-07 08:39:22 -08001060 if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) {
jboeuf6ad120e2015-01-12 17:08:15 -08001061 if (composite_creds != NULL) *composite_creds = creds;
1062 return inner_creds_array->creds_array[i];
1063 }
1064 }
1065 }
1066 return NULL;
1067}
1068
nnoble0c475f02014-12-05 15:37:39 -08001069/* -- IAM credentials. -- */
1070
1071typedef struct {
1072 grpc_credentials base;
1073 grpc_mdctx *md_ctx;
1074 grpc_mdelem *token_md;
1075 grpc_mdelem *authority_selector_md;
1076} grpc_iam_credentials;
1077
1078static void iam_destroy(grpc_credentials *creds) {
1079 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
1080 grpc_mdelem_unref(c->token_md);
1081 grpc_mdelem_unref(c->authority_selector_md);
Craig Tiller9be83ee2015-02-18 14:16:15 -08001082 grpc_mdctx_unref(c->md_ctx);
nnoble0c475f02014-12-05 15:37:39 -08001083 gpr_free(c);
1084}
1085
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +01001086static int iam_has_request_metadata(const grpc_credentials *creds) {
1087 return 1;
1088}
nnoble0c475f02014-12-05 15:37:39 -08001089
1090static int iam_has_request_metadata_only(const grpc_credentials *creds) {
1091 return 1;
1092}
1093
1094static void iam_get_request_metadata(grpc_credentials *creds,
Julien Boeuff47a5cb2015-02-18 12:24:08 -08001095 const char *service_url,
nnoble0c475f02014-12-05 15:37:39 -08001096 grpc_credentials_metadata_cb cb,
1097 void *user_data) {
1098 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
1099 grpc_mdelem *md_array[2];
1100 md_array[0] = c->token_md;
1101 md_array[1] = c->authority_selector_md;
1102 cb(user_data, md_array, 2, GRPC_CREDENTIALS_OK);
1103}
1104
1105static grpc_credentials_vtable iam_vtable = {
1106 iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
1107 iam_get_request_metadata};
1108
1109grpc_credentials *grpc_iam_credentials_create(const char *token,
1110 const char *authority_selector) {
1111 grpc_iam_credentials *c;
1112 GPR_ASSERT(token != NULL);
1113 GPR_ASSERT(authority_selector != NULL);
1114 c = gpr_malloc(sizeof(grpc_iam_credentials));
1115 memset(c, 0, sizeof(grpc_iam_credentials));
1116 c->base.type = GRPC_CREDENTIALS_TYPE_IAM;
1117 c->base.vtable = &iam_vtable;
1118 gpr_ref_init(&c->base.refcount, 1);
1119 c->md_ctx = grpc_mdctx_create();
1120 c->token_md = grpc_mdelem_from_strings(
1121 c->md_ctx, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
1122 c->authority_selector_md = grpc_mdelem_from_strings(
1123 c->md_ctx, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
1124 return &c->base;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001125}