blob: db73c01c1de58f16ee9bcdbb31667ef752cb513c [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
3 * Copyright 2014, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/security/credentials.h"
35
36#include "src/core/httpcli/httpcli.h"
ctiller18b49ab2014-12-09 14:39:16 -080037#include "src/core/iomgr/iomgr.h"
jboeuf1a809c02014-12-19 15:44:30 -080038#include "src/core/security/json_token.h"
Craig Tiller485d7762015-01-23 12:54:05 -080039#include "src/core/support/string.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080040#include <grpc/support/alloc.h>
41#include <grpc/support/log.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080042#include <grpc/support/sync.h>
43#include <grpc/support/time.h>
44
45#include "third_party/cJSON/cJSON.h"
46
47#include <string.h>
48#include <stdio.h>
49
50/* -- Constants. -- */
jboeuf1a809c02014-12-19 15:44:30 -080051
52#define GRPC_OAUTH2_TOKEN_REFRESH_THRESHOLD_SECS 60
53
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080054#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata"
55#define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \
jboeuf1a809c02014-12-19 15:44:30 -080056 "/computeMetadata/v1/instance/service-accounts/default/token"
57
58#define GRPC_SERVICE_ACCOUNT_HOST "www.googleapis.com"
59#define GRPC_SERVICE_ACCOUNT_TOKEN_PATH "/oauth2/v3/token"
60#define GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX \
61 "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" \
62 "assertion="
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080063
64/* -- Common. -- */
65
66typedef struct {
67 grpc_credentials *creds;
68 grpc_credentials_metadata_cb cb;
69 void *user_data;
70} grpc_credentials_metadata_request;
71
72static grpc_credentials_metadata_request *
73grpc_credentials_metadata_request_create(grpc_credentials *creds,
74 grpc_credentials_metadata_cb cb,
75 void *user_data) {
76 grpc_credentials_metadata_request *r =
77 gpr_malloc(sizeof(grpc_credentials_metadata_request));
78 r->creds = grpc_credentials_ref(creds);
79 r->cb = cb;
80 r->user_data = user_data;
81 return r;
82}
83
84static void grpc_credentials_metadata_request_destroy(
85 grpc_credentials_metadata_request *r) {
86 grpc_credentials_unref(r->creds);
87 gpr_free(r);
88}
89
90grpc_credentials *grpc_credentials_ref(grpc_credentials *creds) {
91 if (creds == NULL) return NULL;
92 gpr_ref(&creds->refcount);
93 return creds;
94}
95
96void grpc_credentials_unref(grpc_credentials *creds) {
97 if (creds == NULL) return;
98 if (gpr_unref(&creds->refcount)) creds->vtable->destroy(creds);
99}
100
101void grpc_credentials_release(grpc_credentials *creds) {
102 grpc_credentials_unref(creds);
103}
104
105int grpc_credentials_has_request_metadata(grpc_credentials *creds) {
106 if (creds == NULL) return 0;
107 return creds->vtable->has_request_metadata(creds);
108}
109
110int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) {
111 if (creds == NULL) return 0;
112 return creds->vtable->has_request_metadata_only(creds);
113}
114
115void grpc_credentials_get_request_metadata(grpc_credentials *creds,
116 grpc_credentials_metadata_cb cb,
117 void *user_data) {
nnoble0c475f02014-12-05 15:37:39 -0800118 if (creds == NULL || !grpc_credentials_has_request_metadata(creds) ||
119 creds->vtable->get_request_metadata == NULL) {
120 if (cb != NULL) {
121 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
122 }
123 return;
124 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800125 creds->vtable->get_request_metadata(creds, cb, user_data);
126}
127
128void grpc_server_credentials_release(grpc_server_credentials *creds) {
129 if (creds == NULL) return;
130 creds->vtable->destroy(creds);
131}
132
133/* -- Ssl credentials. -- */
134
135typedef struct {
136 grpc_credentials base;
137 grpc_ssl_config config;
138} grpc_ssl_credentials;
139
140typedef struct {
141 grpc_server_credentials base;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800142 grpc_ssl_server_config config;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800143} grpc_ssl_server_credentials;
144
145static void ssl_destroy(grpc_credentials *creds) {
146 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
147 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
148 if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
149 if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
150 gpr_free(creds);
151}
152
153static void ssl_server_destroy(grpc_server_credentials *creds) {
154 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800155 size_t i;
156 for (i = 0; i < c->config.num_key_cert_pairs; i++) {
157 if (c->config.pem_private_keys[i] != NULL) {
158 gpr_free(c->config.pem_private_keys[i]);
159 }
160 if (c->config.pem_cert_chains[i]!= NULL) {
161 gpr_free(c->config.pem_cert_chains[i]);
162 }
163 }
164 if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys);
165 if (c->config.pem_private_keys_sizes != NULL) {
166 gpr_free(c->config.pem_private_keys_sizes);
167 }
168 if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains);
169 if (c->config.pem_cert_chains_sizes != NULL) {
170 gpr_free(c->config.pem_cert_chains_sizes);
171 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800172 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800173 gpr_free(creds);
174}
175
176static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; }
177
178static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
179 return 0;
180}
181
182static grpc_credentials_vtable ssl_vtable = {
183 ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL};
184
185static grpc_server_credentials_vtable ssl_server_vtable = {ssl_server_destroy};
186
187const grpc_ssl_config *grpc_ssl_credentials_get_config(
188 const grpc_credentials *creds) {
189 if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
190 return NULL;
191 } else {
192 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
193 return &c->config;
194 }
195}
196
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800197const grpc_ssl_server_config *grpc_ssl_server_credentials_get_config(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800198 const grpc_server_credentials *creds) {
199 if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
200 return NULL;
201 } else {
202 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
203 return &c->config;
204 }
205}
206
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800207static void ssl_copy_key_material(const char *input, unsigned char **output,
208 size_t *output_size) {
209 *output_size = strlen(input);
210 *output = gpr_malloc(*output_size);
211 memcpy(*output, input, *output_size);
212}
213
214static void ssl_build_config(const char *pem_root_certs,
215 grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800216 grpc_ssl_config *config) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800217 if (pem_root_certs == NULL) {
218 /* TODO(jboeuf): Get them from the environment. */
219 gpr_log(GPR_ERROR, "Default SSL roots not yet implemented.");
220 } else {
221 ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
222 &config->pem_root_certs_size);
223 }
224
225 if (pem_key_cert_pair != NULL) {
226 GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
227 GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
228 ssl_copy_key_material(pem_key_cert_pair->private_key,
229 &config->pem_private_key,
230 &config->pem_private_key_size);
231 ssl_copy_key_material(pem_key_cert_pair->cert_chain,
232 &config->pem_cert_chain,
233 &config->pem_cert_chain_size);
234 }
235}
236
237static void ssl_build_server_config(
238 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
239 size_t num_key_cert_pairs, grpc_ssl_server_config *config) {
240 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800241 if (pem_root_certs != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800242 ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
243 &config->pem_root_certs_size);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800244 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800245 if (num_key_cert_pairs > 0) {
246 GPR_ASSERT(pem_key_cert_pairs != NULL);
247 config->pem_private_keys =
248 gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
249 config->pem_cert_chains =
250 gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
251 config->pem_private_keys_sizes =
252 gpr_malloc(num_key_cert_pairs * sizeof(size_t));
253 config->pem_cert_chains_sizes =
254 gpr_malloc(num_key_cert_pairs * sizeof(size_t));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800255 }
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800256 config->num_key_cert_pairs = num_key_cert_pairs;
257 for (i = 0; i < num_key_cert_pairs; i++) {
258 GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
259 GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
260 ssl_copy_key_material(pem_key_cert_pairs[i].private_key,
261 &config->pem_private_keys[i],
262 &config->pem_private_keys_sizes[i]);
263 ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain,
264 &config->pem_cert_chains[i],
265 &config->pem_cert_chains_sizes[i]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800266 }
267}
268
269grpc_credentials *grpc_ssl_credentials_create(
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800270 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800271 grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
272 memset(c, 0, sizeof(grpc_ssl_credentials));
273 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
274 c->base.vtable = &ssl_vtable;
275 gpr_ref_init(&c->base.refcount, 1);
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800276 ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800277 return &c->base;
278}
279
280grpc_server_credentials *grpc_ssl_server_credentials_create(
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800281 const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
282 size_t num_key_cert_pairs) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800283 grpc_ssl_server_credentials *c =
284 gpr_malloc(sizeof(grpc_ssl_server_credentials));
285 memset(c, 0, sizeof(grpc_ssl_server_credentials));
286 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
287 c->base.vtable = &ssl_server_vtable;
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800288 ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
289 num_key_cert_pairs, &c->config);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800290 return &c->base;
291}
292
jboeuf1a809c02014-12-19 15:44:30 -0800293/* -- Oauth2TokenFetcher credentials -- */
294
295/* This object is a base for credentials that need to acquire an oauth2 token
296 from an http service. */
297
298typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req,
299 grpc_httpcli_response_cb response_cb,
300 gpr_timespec deadline);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800301
302typedef struct {
303 grpc_credentials base;
304 gpr_mu mu;
305 grpc_mdctx *md_ctx;
306 grpc_mdelem *access_token_md;
307 gpr_timespec token_expiration;
jboeuf1a809c02014-12-19 15:44:30 -0800308 grpc_fetch_oauth2_func fetch_func;
309} grpc_oauth2_token_fetcher_credentials;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800310
jboeuf1a809c02014-12-19 15:44:30 -0800311static void oauth2_token_fetcher_destroy(grpc_credentials *creds) {
312 grpc_oauth2_token_fetcher_credentials *c =
313 (grpc_oauth2_token_fetcher_credentials *)creds;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800314 if (c->access_token_md != NULL) {
315 grpc_mdelem_unref(c->access_token_md);
316 }
317 gpr_mu_destroy(&c->mu);
318 grpc_mdctx_orphan(c->md_ctx);
319 gpr_free(c);
320}
321
jboeuf1a809c02014-12-19 15:44:30 -0800322static int oauth2_token_fetcher_has_request_metadata(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800323 const grpc_credentials *creds) {
324 return 1;
325}
326
jboeuf1a809c02014-12-19 15:44:30 -0800327static int oauth2_token_fetcher_has_request_metadata_only(
328 const grpc_credentials *creds) {
329 return 1;
330}
331
332grpc_credentials_status
333grpc_oauth2_token_fetcher_credentials_parse_server_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800334 const grpc_httpcli_response *response, grpc_mdctx *ctx,
335 grpc_mdelem **token_elem, gpr_timespec *token_lifetime) {
336 char *null_terminated_body = NULL;
337 char *new_access_token = NULL;
338 grpc_credentials_status status = GRPC_CREDENTIALS_OK;
339 cJSON *json = NULL;
340
jboeuf1a809c02014-12-19 15:44:30 -0800341 if (response->body_length > 0) {
342 null_terminated_body = gpr_malloc(response->body_length + 1);
343 null_terminated_body[response->body_length] = '\0';
344 memcpy(null_terminated_body, response->body, response->body_length);
345 }
346
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800347 if (response->status != 200) {
jboeuf1a809c02014-12-19 15:44:30 -0800348 gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
349 response->status,
350 null_terminated_body != NULL ? null_terminated_body : "");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800351 status = GRPC_CREDENTIALS_ERROR;
352 goto end;
353 } else {
354 cJSON *access_token = NULL;
355 cJSON *token_type = NULL;
356 cJSON *expires_in = NULL;
357 size_t new_access_token_size = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800358 json = cJSON_Parse(null_terminated_body);
359 if (json == NULL) {
360 gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
361 status = GRPC_CREDENTIALS_ERROR;
362 goto end;
363 }
364 if (json->type != cJSON_Object) {
365 gpr_log(GPR_ERROR, "Response should be a JSON object");
366 status = GRPC_CREDENTIALS_ERROR;
367 goto end;
368 }
369 access_token = cJSON_GetObjectItem(json, "access_token");
370 if (access_token == NULL || access_token->type != cJSON_String) {
371 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
372 status = GRPC_CREDENTIALS_ERROR;
373 goto end;
374 }
375 token_type = cJSON_GetObjectItem(json, "token_type");
376 if (token_type == NULL || token_type->type != cJSON_String) {
377 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
378 status = GRPC_CREDENTIALS_ERROR;
379 goto end;
380 }
381 expires_in = cJSON_GetObjectItem(json, "expires_in");
382 if (expires_in == NULL || expires_in->type != cJSON_Number) {
383 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
384 status = GRPC_CREDENTIALS_ERROR;
385 goto end;
386 }
387 new_access_token_size = strlen(token_type->valuestring) + 1 +
388 strlen(access_token->valuestring) + 1;
389 new_access_token = gpr_malloc(new_access_token_size);
390 /* C89 does not have snprintf :(. */
391 sprintf(new_access_token, "%s %s", token_type->valuestring,
392 access_token->valuestring);
393 token_lifetime->tv_sec = expires_in->valueint;
394 token_lifetime->tv_nsec = 0;
395 if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
396 *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
397 new_access_token);
398 status = GRPC_CREDENTIALS_OK;
399 }
400
401end:
402 if (status != GRPC_CREDENTIALS_OK && (*token_elem != NULL)) {
403 grpc_mdelem_unref(*token_elem);
404 *token_elem = NULL;
405 }
406 if (null_terminated_body != NULL) gpr_free(null_terminated_body);
407 if (new_access_token != NULL) gpr_free(new_access_token);
408 if (json != NULL) cJSON_Delete(json);
409 return status;
410}
411
jboeuf1a809c02014-12-19 15:44:30 -0800412static void on_oauth2_token_fetcher_http_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800413 void *user_data, const grpc_httpcli_response *response) {
414 grpc_credentials_metadata_request *r =
415 (grpc_credentials_metadata_request *)user_data;
jboeuf1a809c02014-12-19 15:44:30 -0800416 grpc_oauth2_token_fetcher_credentials *c =
417 (grpc_oauth2_token_fetcher_credentials *)r->creds;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800418 gpr_timespec token_lifetime;
419 grpc_credentials_status status;
420
421 gpr_mu_lock(&c->mu);
jboeuf1a809c02014-12-19 15:44:30 -0800422 status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800423 response, c->md_ctx, &c->access_token_md, &token_lifetime);
424 if (status == GRPC_CREDENTIALS_OK) {
425 c->token_expiration = gpr_time_add(gpr_now(), token_lifetime);
426 r->cb(r->user_data, &c->access_token_md, 1, status);
427 } else {
428 c->token_expiration = gpr_inf_past;
429 r->cb(r->user_data, NULL, 0, status);
430 }
431 gpr_mu_unlock(&c->mu);
432 grpc_credentials_metadata_request_destroy(r);
433}
434
jboeuf1a809c02014-12-19 15:44:30 -0800435static void oauth2_token_fetcher_get_request_metadata(
436 grpc_credentials *creds, grpc_credentials_metadata_cb cb, void *user_data) {
437 grpc_oauth2_token_fetcher_credentials *c =
438 (grpc_oauth2_token_fetcher_credentials *)creds;
439 gpr_timespec refresh_threshold = {GRPC_OAUTH2_TOKEN_REFRESH_THRESHOLD_SECS,
440 0};
441 grpc_mdelem *cached_access_token_md = NULL;
442 {
443 gpr_mu_lock(&c->mu);
444 if (c->access_token_md != NULL &&
445 (gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()),
446 refresh_threshold) > 0)) {
447 cached_access_token_md = grpc_mdelem_ref(c->access_token_md);
448 }
449 gpr_mu_unlock(&c->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800450 }
jboeuf1a809c02014-12-19 15:44:30 -0800451 if (cached_access_token_md != NULL) {
452 cb(user_data, &cached_access_token_md, 1, GRPC_CREDENTIALS_OK);
453 grpc_mdelem_unref(cached_access_token_md);
454 } else {
455 c->fetch_func(
456 grpc_credentials_metadata_request_create(creds, cb, user_data),
457 on_oauth2_token_fetcher_http_response,
458 gpr_time_add(gpr_now(), refresh_threshold));
459 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800460}
461
jboeuf1a809c02014-12-19 15:44:30 -0800462static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
463 grpc_fetch_oauth2_func fetch_func) {
464 memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800465 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800466 gpr_ref_init(&c->base.refcount, 1);
467 gpr_mu_init(&c->mu);
468 c->md_ctx = grpc_mdctx_create();
469 c->token_expiration = gpr_inf_past;
jboeuf1a809c02014-12-19 15:44:30 -0800470 c->fetch_func = fetch_func;
471}
472
473/* -- ComputeEngine credentials. -- */
474
475static grpc_credentials_vtable compute_engine_vtable = {
476 oauth2_token_fetcher_destroy, oauth2_token_fetcher_has_request_metadata,
477 oauth2_token_fetcher_has_request_metadata_only,
478 oauth2_token_fetcher_get_request_metadata};
479
480static void compute_engine_fetch_oauth2(
481 grpc_credentials_metadata_request *metadata_req,
482 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
483 grpc_httpcli_header header = {"Metadata-Flavor", "Google"};
484 grpc_httpcli_request request;
485 memset(&request, 0, sizeof(grpc_httpcli_request));
486 request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
487 request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
488 request.hdr_count = 1;
489 request.hdrs = &header;
490 grpc_httpcli_get(&request, deadline, response_cb, metadata_req);
491}
492
493grpc_credentials *grpc_compute_engine_credentials_create(void) {
494 grpc_oauth2_token_fetcher_credentials *c =
495 gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
496 init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
497 c->base.vtable = &compute_engine_vtable;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800498 return &c->base;
499}
500
jboeuf1a809c02014-12-19 15:44:30 -0800501/* -- ServiceAccount credentials. -- */
502
503typedef struct {
504 grpc_oauth2_token_fetcher_credentials base;
505 grpc_auth_json_key key;
506 char *scope;
507 gpr_timespec token_lifetime;
508} grpc_service_account_credentials;
509
510static void service_account_destroy(grpc_credentials *creds) {
511 grpc_service_account_credentials *c =
512 (grpc_service_account_credentials *)creds;
513 if (c->scope != NULL) gpr_free(c->scope);
514 grpc_auth_json_key_destruct(&c->key);
515 oauth2_token_fetcher_destroy(&c->base.base);
516}
517
518static grpc_credentials_vtable service_account_vtable = {
519 service_account_destroy, oauth2_token_fetcher_has_request_metadata,
520 oauth2_token_fetcher_has_request_metadata_only,
521 oauth2_token_fetcher_get_request_metadata};
522
523static void service_account_fetch_oauth2(
524 grpc_credentials_metadata_request *metadata_req,
525 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
526 grpc_service_account_credentials *c =
527 (grpc_service_account_credentials *)metadata_req->creds;
528 grpc_httpcli_header header = {"Content-Type",
529 "application/x-www-form-urlencoded"};
530 grpc_httpcli_request request;
531 char *body = NULL;
532 char *jwt = grpc_jwt_encode_and_sign(&c->key, c->scope, c->token_lifetime);
533 if (jwt == NULL) {
534 grpc_httpcli_response response;
535 memset(&response, 0, sizeof(grpc_httpcli_response));
536 response.status = 400; /* Invalid request. */
537 gpr_log(GPR_ERROR, "Could not create signed jwt.");
538 /* Do not even send the request, just call the response callback. */
539 response_cb(metadata_req, &response);
540 return;
541 }
542 body = gpr_malloc(strlen(GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX) +
543 strlen(jwt) + 1);
544 sprintf(body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt);
545 memset(&request, 0, sizeof(grpc_httpcli_request));
546 request.host = GRPC_SERVICE_ACCOUNT_HOST;
547 request.path = GRPC_SERVICE_ACCOUNT_TOKEN_PATH;
548 request.hdr_count = 1;
549 request.hdrs = &header;
550 request.use_ssl = 1;
551 grpc_httpcli_post(&request, body, strlen(body), deadline, response_cb,
552 metadata_req);
553 gpr_free(body);
554 gpr_free(jwt);
555}
556
557grpc_credentials *grpc_service_account_credentials_create(
558 const char *json_key, const char *scope, gpr_timespec token_lifetime) {
559 grpc_service_account_credentials *c;
560 grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
561
562 if (scope == NULL || (strlen(scope) == 0) ||
563 !grpc_auth_json_key_is_valid(&key)) {
564 gpr_log(GPR_ERROR,
565 "Invalid input for service account credentials creation");
566 return NULL;
567 }
568 c = gpr_malloc(sizeof(grpc_service_account_credentials));
569 memset(c, 0, sizeof(grpc_service_account_credentials));
570 init_oauth2_token_fetcher(&c->base, service_account_fetch_oauth2);
571 c->base.base.vtable = &service_account_vtable;
572 c->scope = gpr_strdup(scope);
573 c->key = key;
574 c->token_lifetime = token_lifetime;
575 return &c->base.base;
576}
577
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800578/* -- Fake Oauth2 credentials. -- */
579
580typedef struct {
581 grpc_credentials base;
582 grpc_mdctx *md_ctx;
583 grpc_mdelem *access_token_md;
584 int is_async;
585} grpc_fake_oauth2_credentials;
586
587static void fake_oauth2_destroy(grpc_credentials *creds) {
588 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
589 if (c->access_token_md != NULL) {
590 grpc_mdelem_unref(c->access_token_md);
591 }
592 grpc_mdctx_orphan(c->md_ctx);
593 gpr_free(c);
594}
595
596static int fake_oauth2_has_request_metadata(const grpc_credentials *creds) {
597 return 1;
598}
599
600static int fake_oauth2_has_request_metadata_only(
601 const grpc_credentials *creds) {
602 return 1;
603}
604
ctiller58393c22015-01-07 14:03:30 -0800605void on_simulated_token_fetch_done(void *user_data, int success) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800606 grpc_credentials_metadata_request *r =
607 (grpc_credentials_metadata_request *)user_data;
608 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)r->creds;
ctiller58393c22015-01-07 14:03:30 -0800609 GPR_ASSERT(success);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800610 r->cb(r->user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
611 grpc_credentials_metadata_request_destroy(r);
612}
613
614static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
615 grpc_credentials_metadata_cb cb,
616 void *user_data) {
617 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
618
619 if (c->is_async) {
ctiller18b49ab2014-12-09 14:39:16 -0800620 grpc_iomgr_add_callback(
621 on_simulated_token_fetch_done,
622 grpc_credentials_metadata_request_create(creds, cb, user_data));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800623 } else {
624 cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
625 }
626}
627
628static grpc_credentials_vtable fake_oauth2_vtable = {
629 fake_oauth2_destroy, fake_oauth2_has_request_metadata,
630 fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata};
631
632grpc_credentials *grpc_fake_oauth2_credentials_create(
633 const char *token_md_value, int is_async) {
634 grpc_fake_oauth2_credentials *c =
635 gpr_malloc(sizeof(grpc_fake_oauth2_credentials));
636 memset(c, 0, sizeof(grpc_fake_oauth2_credentials));
637 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
638 c->base.vtable = &fake_oauth2_vtable;
639 gpr_ref_init(&c->base.refcount, 1);
640 c->md_ctx = grpc_mdctx_create();
641 c->access_token_md = grpc_mdelem_from_strings(
642 c->md_ctx, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
643 c->is_async = is_async;
644 return &c->base;
645}
646
647/* -- Fake transport security credentials. -- */
648
649static void fake_transport_security_credentials_destroy(
650 grpc_credentials *creds) {
651 gpr_free(creds);
652}
653
654static void fake_transport_security_server_credentials_destroy(
655 grpc_server_credentials *creds) {
656 gpr_free(creds);
657}
658
659static int fake_transport_security_has_request_metadata(
660 const grpc_credentials *creds) {
661 return 0;
662}
663
664static int fake_transport_security_has_request_metadata_only(
665 const grpc_credentials *creds) {
666 return 0;
667}
668
669static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
670 fake_transport_security_credentials_destroy,
671 fake_transport_security_has_request_metadata,
672 fake_transport_security_has_request_metadata_only, NULL};
673
674static grpc_server_credentials_vtable
675 fake_transport_security_server_credentials_vtable = {
676 fake_transport_security_server_credentials_destroy};
677
678grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
679 grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
680 memset(c, 0, sizeof(grpc_credentials));
681 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
682 c->vtable = &fake_transport_security_credentials_vtable;
683 gpr_ref_init(&c->refcount, 1);
684 return c;
685}
686
Craig Tiller3eef2c42015-01-15 11:37:54 -0800687grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
688 void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800689 grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
690 memset(c, 0, sizeof(grpc_server_credentials));
691 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
692 c->vtable = &fake_transport_security_server_credentials_vtable;
693 return c;
694}
695
nnoble0c475f02014-12-05 15:37:39 -0800696/* -- Composite credentials. -- */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800697
nnoble0c475f02014-12-05 15:37:39 -0800698typedef struct {
699 grpc_credentials base;
700 grpc_credentials_array inner;
701} grpc_composite_credentials;
702
703typedef struct {
704 grpc_composite_credentials *composite_creds;
705 size_t creds_index;
706 grpc_mdelem **md_elems;
707 size_t num_md;
708 void *user_data;
709 grpc_credentials_metadata_cb cb;
710} grpc_composite_credentials_metadata_context;
711
712static void composite_destroy(grpc_credentials *creds) {
713 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
714 size_t i;
715 for (i = 0; i < c->inner.num_creds; i++) {
716 grpc_credentials_unref(c->inner.creds_array[i]);
717 }
718 gpr_free(c->inner.creds_array);
719 gpr_free(creds);
720}
721
722static int composite_has_request_metadata(const grpc_credentials *creds) {
723 const grpc_composite_credentials *c =
724 (const grpc_composite_credentials *)creds;
725 size_t i;
726 for (i = 0; i < c->inner.num_creds; i++) {
727 if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) {
728 return 1;
729 }
730 }
731 return 0;
732}
733
734static int composite_has_request_metadata_only(const grpc_credentials *creds) {
735 const grpc_composite_credentials *c =
736 (const grpc_composite_credentials *)creds;
737 size_t i;
738 for (i = 0; i < c->inner.num_creds; i++) {
739 if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) {
740 return 0;
741 }
742 }
743 return 1;
744}
745
746static void composite_md_context_destroy(
747 grpc_composite_credentials_metadata_context *ctx) {
748 size_t i;
749 for (i = 0; i < ctx->num_md; i++) {
750 grpc_mdelem_unref(ctx->md_elems[i]);
751 }
752 gpr_free(ctx->md_elems);
753 gpr_free(ctx);
754}
755
756static void composite_metadata_cb(void *user_data, grpc_mdelem **md_elems,
757 size_t num_md,
758 grpc_credentials_status status) {
759 grpc_composite_credentials_metadata_context *ctx =
760 (grpc_composite_credentials_metadata_context *)user_data;
761 size_t i;
762 if (status != GRPC_CREDENTIALS_OK) {
763 ctx->cb(ctx->user_data, NULL, 0, status);
764 return;
765 }
766
767 /* Copy the metadata in the context. */
768 if (num_md > 0) {
769 ctx->md_elems = gpr_realloc(ctx->md_elems,
770 (ctx->num_md + num_md) * sizeof(grpc_mdelem *));
771 for (i = 0; i < num_md; i++) {
772 ctx->md_elems[i + ctx->num_md] = grpc_mdelem_ref(md_elems[i]);
773 }
774 ctx->num_md += num_md;
775 }
776
777 /* See if we need to get some more metadata. */
778 while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
779 grpc_credentials *inner_creds =
780 ctx->composite_creds->inner.creds_array[ctx->creds_index++];
781 if (grpc_credentials_has_request_metadata(inner_creds)) {
782 grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
783 ctx);
784 return;
785 }
786 }
787
788 /* We're done!. */
789 ctx->cb(ctx->user_data, ctx->md_elems, ctx->num_md, GRPC_CREDENTIALS_OK);
790 composite_md_context_destroy(ctx);
791}
792
793static void composite_get_request_metadata(grpc_credentials *creds,
794 grpc_credentials_metadata_cb cb,
795 void *user_data) {
796 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
797 grpc_composite_credentials_metadata_context *ctx;
798 if (!grpc_credentials_has_request_metadata(creds)) {
799 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
800 return;
801 }
802 ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context));
803 memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context));
804 ctx->user_data = user_data;
805 ctx->cb = cb;
806 ctx->composite_creds = c;
807 while (ctx->creds_index < c->inner.num_creds) {
808 grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++];
809 if (grpc_credentials_has_request_metadata(inner_creds)) {
810 grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
811 ctx);
812 return;
813 }
814 }
815 GPR_ASSERT(0); /* Should have exited before. */
816}
817
818static grpc_credentials_vtable composite_credentials_vtable = {
819 composite_destroy, composite_has_request_metadata,
820 composite_has_request_metadata_only, composite_get_request_metadata};
821
822static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
823 grpc_credentials_array result;
824 grpc_credentials *creds = *creds_addr;
825 result.creds_array = creds_addr;
826 result.num_creds = 1;
827 if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
828 result = *grpc_composite_credentials_get_credentials(creds);
829 }
830 return result;
831}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800832
833grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
834 grpc_credentials *creds2) {
nnoble0c475f02014-12-05 15:37:39 -0800835 size_t i;
836 grpc_credentials_array creds1_array;
837 grpc_credentials_array creds2_array;
838 grpc_composite_credentials *c;
839 GPR_ASSERT(creds1 != NULL);
840 GPR_ASSERT(creds2 != NULL);
841 c = gpr_malloc(sizeof(grpc_composite_credentials));
842 memset(c, 0, sizeof(grpc_composite_credentials));
843 c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE;
844 c->base.vtable = &composite_credentials_vtable;
845 gpr_ref_init(&c->base.refcount, 1);
846 creds1_array = get_creds_array(&creds1);
847 creds2_array = get_creds_array(&creds2);
848 c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
849 c->inner.creds_array =
850 gpr_malloc(c->inner.num_creds * sizeof(grpc_credentials *));
851 for (i = 0; i < creds1_array.num_creds; i++) {
852 c->inner.creds_array[i] = grpc_credentials_ref(creds1_array.creds_array[i]);
853 }
854 for (i = 0; i < creds2_array.num_creds; i++) {
855 c->inner.creds_array[i + creds1_array.num_creds] =
856 grpc_credentials_ref(creds2_array.creds_array[i]);
857 }
858 return &c->base;
859}
860
861const grpc_credentials_array *grpc_composite_credentials_get_credentials(
862 grpc_credentials *creds) {
863 const grpc_composite_credentials *c =
864 (const grpc_composite_credentials *)creds;
865 GPR_ASSERT(!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
866 return &c->inner;
867}
868
jboeuf6ad120e2015-01-12 17:08:15 -0800869grpc_credentials *grpc_credentials_contains_type(
870 grpc_credentials *creds, const char *type,
871 grpc_credentials **composite_creds) {
872 size_t i;
873 if (!strcmp(creds->type, type)) {
874 if (composite_creds != NULL) *composite_creds = NULL;
875 return creds;
876 } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
877 const grpc_credentials_array *inner_creds_array =
878 grpc_composite_credentials_get_credentials(creds);
879 for (i = 0; i < inner_creds_array->num_creds; i++) {
880 if (!strcmp(type, inner_creds_array->creds_array[i]->type)) {
881 if (composite_creds != NULL) *composite_creds = creds;
882 return inner_creds_array->creds_array[i];
883 }
884 }
885 }
886 return NULL;
887}
888
nnoble0c475f02014-12-05 15:37:39 -0800889/* -- IAM credentials. -- */
890
891typedef struct {
892 grpc_credentials base;
893 grpc_mdctx *md_ctx;
894 grpc_mdelem *token_md;
895 grpc_mdelem *authority_selector_md;
896} grpc_iam_credentials;
897
898static void iam_destroy(grpc_credentials *creds) {
899 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
900 grpc_mdelem_unref(c->token_md);
901 grpc_mdelem_unref(c->authority_selector_md);
902 grpc_mdctx_orphan(c->md_ctx);
903 gpr_free(c);
904}
905
906static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; }
907
908static int iam_has_request_metadata_only(const grpc_credentials *creds) {
909 return 1;
910}
911
912static void iam_get_request_metadata(grpc_credentials *creds,
913 grpc_credentials_metadata_cb cb,
914 void *user_data) {
915 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
916 grpc_mdelem *md_array[2];
917 md_array[0] = c->token_md;
918 md_array[1] = c->authority_selector_md;
919 cb(user_data, md_array, 2, GRPC_CREDENTIALS_OK);
920}
921
922static grpc_credentials_vtable iam_vtable = {
923 iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
924 iam_get_request_metadata};
925
926grpc_credentials *grpc_iam_credentials_create(const char *token,
927 const char *authority_selector) {
928 grpc_iam_credentials *c;
929 GPR_ASSERT(token != NULL);
930 GPR_ASSERT(authority_selector != NULL);
931 c = gpr_malloc(sizeof(grpc_iam_credentials));
932 memset(c, 0, sizeof(grpc_iam_credentials));
933 c->base.type = GRPC_CREDENTIALS_TYPE_IAM;
934 c->base.vtable = &iam_vtable;
935 gpr_ref_init(&c->base.refcount, 1);
936 c->md_ctx = grpc_mdctx_create();
937 c->token_md = grpc_mdelem_from_strings(
938 c->md_ctx, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
939 c->authority_selector_md = grpc_mdelem_from_strings(
940 c->md_ctx, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
941 return &c->base;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800942}
943
944/* -- Default credentials TODO(jboeuf). -- */
945
946grpc_credentials *grpc_default_credentials_create(void) { return NULL; }