blob: cc48623ea41b024dbbf3331fd31fac85445664b1 [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"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080039#include <grpc/support/alloc.h>
40#include <grpc/support/log.h>
41#include <grpc/support/string.h>
42#include <grpc/support/sync.h>
43#include <grpc/support/time.h>
44
Nicolas Noble614c2bf2015-01-21 15:48:36 -080045#include "src/core/json/json.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080046
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 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800160 if (c->config.pem_cert_chains[i] != NULL) {
Julien Boeuf8fbcc432015-01-15 16:44:13 -0800161 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;
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800339 grpc_json *json = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800340
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 {
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800354 grpc_json *access_token = NULL;
355 grpc_json *token_type = NULL;
356 grpc_json *expires_in = NULL;
357 grpc_json *ptr;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800358 size_t new_access_token_size = 0;
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800359 json = grpc_json_parse_string(null_terminated_body);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800360 if (json == NULL) {
361 gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
362 status = GRPC_CREDENTIALS_ERROR;
363 goto end;
364 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800365 if (json->type != GRPC_JSON_OBJECT) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800366 gpr_log(GPR_ERROR, "Response should be a JSON object");
367 status = GRPC_CREDENTIALS_ERROR;
368 goto end;
369 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800370 for (ptr = json->child; ptr; ptr = ptr->next) {
371 if (strcmp(ptr->key, "access_token") == 0) {
372 access_token = ptr;
373 } else if (strcmp(ptr->key, "token_type") == 0) {
374 token_type = ptr;
375 } else if (strcmp(ptr->key, "expires_in") == 0) {
376 expires_in = ptr;
377 }
378 }
379 if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800380 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
381 status = GRPC_CREDENTIALS_ERROR;
382 goto end;
383 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800384 if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800385 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
386 status = GRPC_CREDENTIALS_ERROR;
387 goto end;
388 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800389 if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800390 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
391 status = GRPC_CREDENTIALS_ERROR;
392 goto end;
393 }
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800394 new_access_token_size =
395 strlen(token_type->value) + 1 + strlen(access_token->value) + 1;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800396 new_access_token = gpr_malloc(new_access_token_size);
397 /* C89 does not have snprintf :(. */
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800398 sprintf(new_access_token, "%s %s", token_type->value, access_token->value);
399 token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800400 token_lifetime->tv_nsec = 0;
401 if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
402 *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
403 new_access_token);
404 status = GRPC_CREDENTIALS_OK;
405 }
406
407end:
408 if (status != GRPC_CREDENTIALS_OK && (*token_elem != NULL)) {
409 grpc_mdelem_unref(*token_elem);
410 *token_elem = NULL;
411 }
412 if (null_terminated_body != NULL) gpr_free(null_terminated_body);
413 if (new_access_token != NULL) gpr_free(new_access_token);
Nicolas Noble614c2bf2015-01-21 15:48:36 -0800414 if (json != NULL) grpc_json_delete(json);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800415 return status;
416}
417
jboeuf1a809c02014-12-19 15:44:30 -0800418static void on_oauth2_token_fetcher_http_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800419 void *user_data, const grpc_httpcli_response *response) {
420 grpc_credentials_metadata_request *r =
421 (grpc_credentials_metadata_request *)user_data;
jboeuf1a809c02014-12-19 15:44:30 -0800422 grpc_oauth2_token_fetcher_credentials *c =
423 (grpc_oauth2_token_fetcher_credentials *)r->creds;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800424 gpr_timespec token_lifetime;
425 grpc_credentials_status status;
426
427 gpr_mu_lock(&c->mu);
jboeuf1a809c02014-12-19 15:44:30 -0800428 status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800429 response, c->md_ctx, &c->access_token_md, &token_lifetime);
430 if (status == GRPC_CREDENTIALS_OK) {
431 c->token_expiration = gpr_time_add(gpr_now(), token_lifetime);
432 r->cb(r->user_data, &c->access_token_md, 1, status);
433 } else {
434 c->token_expiration = gpr_inf_past;
435 r->cb(r->user_data, NULL, 0, status);
436 }
437 gpr_mu_unlock(&c->mu);
438 grpc_credentials_metadata_request_destroy(r);
439}
440
jboeuf1a809c02014-12-19 15:44:30 -0800441static void oauth2_token_fetcher_get_request_metadata(
442 grpc_credentials *creds, grpc_credentials_metadata_cb cb, void *user_data) {
443 grpc_oauth2_token_fetcher_credentials *c =
444 (grpc_oauth2_token_fetcher_credentials *)creds;
445 gpr_timespec refresh_threshold = {GRPC_OAUTH2_TOKEN_REFRESH_THRESHOLD_SECS,
446 0};
447 grpc_mdelem *cached_access_token_md = NULL;
448 {
449 gpr_mu_lock(&c->mu);
450 if (c->access_token_md != NULL &&
451 (gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()),
452 refresh_threshold) > 0)) {
453 cached_access_token_md = grpc_mdelem_ref(c->access_token_md);
454 }
455 gpr_mu_unlock(&c->mu);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800456 }
jboeuf1a809c02014-12-19 15:44:30 -0800457 if (cached_access_token_md != NULL) {
458 cb(user_data, &cached_access_token_md, 1, GRPC_CREDENTIALS_OK);
459 grpc_mdelem_unref(cached_access_token_md);
460 } else {
461 c->fetch_func(
462 grpc_credentials_metadata_request_create(creds, cb, user_data),
463 on_oauth2_token_fetcher_http_response,
464 gpr_time_add(gpr_now(), refresh_threshold));
465 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800466}
467
jboeuf1a809c02014-12-19 15:44:30 -0800468static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
469 grpc_fetch_oauth2_func fetch_func) {
470 memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800471 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800472 gpr_ref_init(&c->base.refcount, 1);
473 gpr_mu_init(&c->mu);
474 c->md_ctx = grpc_mdctx_create();
475 c->token_expiration = gpr_inf_past;
jboeuf1a809c02014-12-19 15:44:30 -0800476 c->fetch_func = fetch_func;
477}
478
479/* -- ComputeEngine credentials. -- */
480
481static grpc_credentials_vtable compute_engine_vtable = {
482 oauth2_token_fetcher_destroy, oauth2_token_fetcher_has_request_metadata,
483 oauth2_token_fetcher_has_request_metadata_only,
484 oauth2_token_fetcher_get_request_metadata};
485
486static void compute_engine_fetch_oauth2(
487 grpc_credentials_metadata_request *metadata_req,
488 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
489 grpc_httpcli_header header = {"Metadata-Flavor", "Google"};
490 grpc_httpcli_request request;
491 memset(&request, 0, sizeof(grpc_httpcli_request));
492 request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
493 request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
494 request.hdr_count = 1;
495 request.hdrs = &header;
496 grpc_httpcli_get(&request, deadline, response_cb, metadata_req);
497}
498
499grpc_credentials *grpc_compute_engine_credentials_create(void) {
500 grpc_oauth2_token_fetcher_credentials *c =
501 gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
502 init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
503 c->base.vtable = &compute_engine_vtable;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800504 return &c->base;
505}
506
jboeuf1a809c02014-12-19 15:44:30 -0800507/* -- ServiceAccount credentials. -- */
508
509typedef struct {
510 grpc_oauth2_token_fetcher_credentials base;
511 grpc_auth_json_key key;
512 char *scope;
513 gpr_timespec token_lifetime;
514} grpc_service_account_credentials;
515
516static void service_account_destroy(grpc_credentials *creds) {
517 grpc_service_account_credentials *c =
518 (grpc_service_account_credentials *)creds;
519 if (c->scope != NULL) gpr_free(c->scope);
520 grpc_auth_json_key_destruct(&c->key);
521 oauth2_token_fetcher_destroy(&c->base.base);
522}
523
524static grpc_credentials_vtable service_account_vtable = {
525 service_account_destroy, oauth2_token_fetcher_has_request_metadata,
526 oauth2_token_fetcher_has_request_metadata_only,
527 oauth2_token_fetcher_get_request_metadata};
528
529static void service_account_fetch_oauth2(
530 grpc_credentials_metadata_request *metadata_req,
531 grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
532 grpc_service_account_credentials *c =
533 (grpc_service_account_credentials *)metadata_req->creds;
534 grpc_httpcli_header header = {"Content-Type",
535 "application/x-www-form-urlencoded"};
536 grpc_httpcli_request request;
537 char *body = NULL;
538 char *jwt = grpc_jwt_encode_and_sign(&c->key, c->scope, c->token_lifetime);
539 if (jwt == NULL) {
540 grpc_httpcli_response response;
541 memset(&response, 0, sizeof(grpc_httpcli_response));
542 response.status = 400; /* Invalid request. */
543 gpr_log(GPR_ERROR, "Could not create signed jwt.");
544 /* Do not even send the request, just call the response callback. */
545 response_cb(metadata_req, &response);
546 return;
547 }
548 body = gpr_malloc(strlen(GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX) +
549 strlen(jwt) + 1);
550 sprintf(body, "%s%s", GRPC_SERVICE_ACCOUNT_POST_BODY_PREFIX, jwt);
551 memset(&request, 0, sizeof(grpc_httpcli_request));
552 request.host = GRPC_SERVICE_ACCOUNT_HOST;
553 request.path = GRPC_SERVICE_ACCOUNT_TOKEN_PATH;
554 request.hdr_count = 1;
555 request.hdrs = &header;
556 request.use_ssl = 1;
557 grpc_httpcli_post(&request, body, strlen(body), deadline, response_cb,
558 metadata_req);
559 gpr_free(body);
560 gpr_free(jwt);
561}
562
563grpc_credentials *grpc_service_account_credentials_create(
564 const char *json_key, const char *scope, gpr_timespec token_lifetime) {
565 grpc_service_account_credentials *c;
566 grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
567
568 if (scope == NULL || (strlen(scope) == 0) ||
569 !grpc_auth_json_key_is_valid(&key)) {
570 gpr_log(GPR_ERROR,
571 "Invalid input for service account credentials creation");
572 return NULL;
573 }
574 c = gpr_malloc(sizeof(grpc_service_account_credentials));
575 memset(c, 0, sizeof(grpc_service_account_credentials));
576 init_oauth2_token_fetcher(&c->base, service_account_fetch_oauth2);
577 c->base.base.vtable = &service_account_vtable;
578 c->scope = gpr_strdup(scope);
579 c->key = key;
580 c->token_lifetime = token_lifetime;
581 return &c->base.base;
582}
583
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800584/* -- Fake Oauth2 credentials. -- */
585
586typedef struct {
587 grpc_credentials base;
588 grpc_mdctx *md_ctx;
589 grpc_mdelem *access_token_md;
590 int is_async;
591} grpc_fake_oauth2_credentials;
592
593static void fake_oauth2_destroy(grpc_credentials *creds) {
594 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
595 if (c->access_token_md != NULL) {
596 grpc_mdelem_unref(c->access_token_md);
597 }
598 grpc_mdctx_orphan(c->md_ctx);
599 gpr_free(c);
600}
601
602static int fake_oauth2_has_request_metadata(const grpc_credentials *creds) {
603 return 1;
604}
605
606static int fake_oauth2_has_request_metadata_only(
607 const grpc_credentials *creds) {
608 return 1;
609}
610
ctiller58393c22015-01-07 14:03:30 -0800611void on_simulated_token_fetch_done(void *user_data, int success) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800612 grpc_credentials_metadata_request *r =
613 (grpc_credentials_metadata_request *)user_data;
614 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)r->creds;
ctiller58393c22015-01-07 14:03:30 -0800615 GPR_ASSERT(success);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800616 r->cb(r->user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
617 grpc_credentials_metadata_request_destroy(r);
618}
619
620static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
621 grpc_credentials_metadata_cb cb,
622 void *user_data) {
623 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
624
625 if (c->is_async) {
ctiller18b49ab2014-12-09 14:39:16 -0800626 grpc_iomgr_add_callback(
627 on_simulated_token_fetch_done,
628 grpc_credentials_metadata_request_create(creds, cb, user_data));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800629 } else {
630 cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
631 }
632}
633
634static grpc_credentials_vtable fake_oauth2_vtable = {
635 fake_oauth2_destroy, fake_oauth2_has_request_metadata,
636 fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata};
637
638grpc_credentials *grpc_fake_oauth2_credentials_create(
639 const char *token_md_value, int is_async) {
640 grpc_fake_oauth2_credentials *c =
641 gpr_malloc(sizeof(grpc_fake_oauth2_credentials));
642 memset(c, 0, sizeof(grpc_fake_oauth2_credentials));
643 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
644 c->base.vtable = &fake_oauth2_vtable;
645 gpr_ref_init(&c->base.refcount, 1);
646 c->md_ctx = grpc_mdctx_create();
647 c->access_token_md = grpc_mdelem_from_strings(
648 c->md_ctx, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
649 c->is_async = is_async;
650 return &c->base;
651}
652
653/* -- Fake transport security credentials. -- */
654
655static void fake_transport_security_credentials_destroy(
656 grpc_credentials *creds) {
657 gpr_free(creds);
658}
659
660static void fake_transport_security_server_credentials_destroy(
661 grpc_server_credentials *creds) {
662 gpr_free(creds);
663}
664
665static int fake_transport_security_has_request_metadata(
666 const grpc_credentials *creds) {
667 return 0;
668}
669
670static int fake_transport_security_has_request_metadata_only(
671 const grpc_credentials *creds) {
672 return 0;
673}
674
675static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
676 fake_transport_security_credentials_destroy,
677 fake_transport_security_has_request_metadata,
678 fake_transport_security_has_request_metadata_only, NULL};
679
680static grpc_server_credentials_vtable
681 fake_transport_security_server_credentials_vtable = {
682 fake_transport_security_server_credentials_destroy};
683
684grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
685 grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
686 memset(c, 0, sizeof(grpc_credentials));
687 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
688 c->vtable = &fake_transport_security_credentials_vtable;
689 gpr_ref_init(&c->refcount, 1);
690 return c;
691}
692
Craig Tiller3eef2c42015-01-15 11:37:54 -0800693grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
694 void) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800695 grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
696 memset(c, 0, sizeof(grpc_server_credentials));
697 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
698 c->vtable = &fake_transport_security_server_credentials_vtable;
699 return c;
700}
701
nnoble0c475f02014-12-05 15:37:39 -0800702/* -- Composite credentials. -- */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800703
nnoble0c475f02014-12-05 15:37:39 -0800704typedef struct {
705 grpc_credentials base;
706 grpc_credentials_array inner;
707} grpc_composite_credentials;
708
709typedef struct {
710 grpc_composite_credentials *composite_creds;
711 size_t creds_index;
712 grpc_mdelem **md_elems;
713 size_t num_md;
714 void *user_data;
715 grpc_credentials_metadata_cb cb;
716} grpc_composite_credentials_metadata_context;
717
718static void composite_destroy(grpc_credentials *creds) {
719 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
720 size_t i;
721 for (i = 0; i < c->inner.num_creds; i++) {
722 grpc_credentials_unref(c->inner.creds_array[i]);
723 }
724 gpr_free(c->inner.creds_array);
725 gpr_free(creds);
726}
727
728static int composite_has_request_metadata(const grpc_credentials *creds) {
729 const grpc_composite_credentials *c =
730 (const grpc_composite_credentials *)creds;
731 size_t i;
732 for (i = 0; i < c->inner.num_creds; i++) {
733 if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) {
734 return 1;
735 }
736 }
737 return 0;
738}
739
740static int composite_has_request_metadata_only(const grpc_credentials *creds) {
741 const grpc_composite_credentials *c =
742 (const grpc_composite_credentials *)creds;
743 size_t i;
744 for (i = 0; i < c->inner.num_creds; i++) {
745 if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) {
746 return 0;
747 }
748 }
749 return 1;
750}
751
752static void composite_md_context_destroy(
753 grpc_composite_credentials_metadata_context *ctx) {
754 size_t i;
755 for (i = 0; i < ctx->num_md; i++) {
756 grpc_mdelem_unref(ctx->md_elems[i]);
757 }
758 gpr_free(ctx->md_elems);
759 gpr_free(ctx);
760}
761
762static void composite_metadata_cb(void *user_data, grpc_mdelem **md_elems,
763 size_t num_md,
764 grpc_credentials_status status) {
765 grpc_composite_credentials_metadata_context *ctx =
766 (grpc_composite_credentials_metadata_context *)user_data;
767 size_t i;
768 if (status != GRPC_CREDENTIALS_OK) {
769 ctx->cb(ctx->user_data, NULL, 0, status);
770 return;
771 }
772
773 /* Copy the metadata in the context. */
774 if (num_md > 0) {
775 ctx->md_elems = gpr_realloc(ctx->md_elems,
776 (ctx->num_md + num_md) * sizeof(grpc_mdelem *));
777 for (i = 0; i < num_md; i++) {
778 ctx->md_elems[i + ctx->num_md] = grpc_mdelem_ref(md_elems[i]);
779 }
780 ctx->num_md += num_md;
781 }
782
783 /* See if we need to get some more metadata. */
784 while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
785 grpc_credentials *inner_creds =
786 ctx->composite_creds->inner.creds_array[ctx->creds_index++];
787 if (grpc_credentials_has_request_metadata(inner_creds)) {
788 grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
789 ctx);
790 return;
791 }
792 }
793
794 /* We're done!. */
795 ctx->cb(ctx->user_data, ctx->md_elems, ctx->num_md, GRPC_CREDENTIALS_OK);
796 composite_md_context_destroy(ctx);
797}
798
799static void composite_get_request_metadata(grpc_credentials *creds,
800 grpc_credentials_metadata_cb cb,
801 void *user_data) {
802 grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
803 grpc_composite_credentials_metadata_context *ctx;
804 if (!grpc_credentials_has_request_metadata(creds)) {
805 cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
806 return;
807 }
808 ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context));
809 memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context));
810 ctx->user_data = user_data;
811 ctx->cb = cb;
812 ctx->composite_creds = c;
813 while (ctx->creds_index < c->inner.num_creds) {
814 grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++];
815 if (grpc_credentials_has_request_metadata(inner_creds)) {
816 grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
817 ctx);
818 return;
819 }
820 }
821 GPR_ASSERT(0); /* Should have exited before. */
822}
823
824static grpc_credentials_vtable composite_credentials_vtable = {
825 composite_destroy, composite_has_request_metadata,
826 composite_has_request_metadata_only, composite_get_request_metadata};
827
828static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
829 grpc_credentials_array result;
830 grpc_credentials *creds = *creds_addr;
831 result.creds_array = creds_addr;
832 result.num_creds = 1;
833 if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
834 result = *grpc_composite_credentials_get_credentials(creds);
835 }
836 return result;
837}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800838
839grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
840 grpc_credentials *creds2) {
nnoble0c475f02014-12-05 15:37:39 -0800841 size_t i;
842 grpc_credentials_array creds1_array;
843 grpc_credentials_array creds2_array;
844 grpc_composite_credentials *c;
845 GPR_ASSERT(creds1 != NULL);
846 GPR_ASSERT(creds2 != NULL);
847 c = gpr_malloc(sizeof(grpc_composite_credentials));
848 memset(c, 0, sizeof(grpc_composite_credentials));
849 c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE;
850 c->base.vtable = &composite_credentials_vtable;
851 gpr_ref_init(&c->base.refcount, 1);
852 creds1_array = get_creds_array(&creds1);
853 creds2_array = get_creds_array(&creds2);
854 c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
855 c->inner.creds_array =
856 gpr_malloc(c->inner.num_creds * sizeof(grpc_credentials *));
857 for (i = 0; i < creds1_array.num_creds; i++) {
858 c->inner.creds_array[i] = grpc_credentials_ref(creds1_array.creds_array[i]);
859 }
860 for (i = 0; i < creds2_array.num_creds; i++) {
861 c->inner.creds_array[i + creds1_array.num_creds] =
862 grpc_credentials_ref(creds2_array.creds_array[i]);
863 }
864 return &c->base;
865}
866
867const grpc_credentials_array *grpc_composite_credentials_get_credentials(
868 grpc_credentials *creds) {
869 const grpc_composite_credentials *c =
870 (const grpc_composite_credentials *)creds;
871 GPR_ASSERT(!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
872 return &c->inner;
873}
874
jboeuf6ad120e2015-01-12 17:08:15 -0800875grpc_credentials *grpc_credentials_contains_type(
876 grpc_credentials *creds, const char *type,
877 grpc_credentials **composite_creds) {
878 size_t i;
879 if (!strcmp(creds->type, type)) {
880 if (composite_creds != NULL) *composite_creds = NULL;
881 return creds;
882 } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
883 const grpc_credentials_array *inner_creds_array =
884 grpc_composite_credentials_get_credentials(creds);
885 for (i = 0; i < inner_creds_array->num_creds; i++) {
886 if (!strcmp(type, inner_creds_array->creds_array[i]->type)) {
887 if (composite_creds != NULL) *composite_creds = creds;
888 return inner_creds_array->creds_array[i];
889 }
890 }
891 }
892 return NULL;
893}
894
nnoble0c475f02014-12-05 15:37:39 -0800895/* -- IAM credentials. -- */
896
897typedef struct {
898 grpc_credentials base;
899 grpc_mdctx *md_ctx;
900 grpc_mdelem *token_md;
901 grpc_mdelem *authority_selector_md;
902} grpc_iam_credentials;
903
904static void iam_destroy(grpc_credentials *creds) {
905 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
906 grpc_mdelem_unref(c->token_md);
907 grpc_mdelem_unref(c->authority_selector_md);
908 grpc_mdctx_orphan(c->md_ctx);
909 gpr_free(c);
910}
911
912static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; }
913
914static int iam_has_request_metadata_only(const grpc_credentials *creds) {
915 return 1;
916}
917
918static void iam_get_request_metadata(grpc_credentials *creds,
919 grpc_credentials_metadata_cb cb,
920 void *user_data) {
921 grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
922 grpc_mdelem *md_array[2];
923 md_array[0] = c->token_md;
924 md_array[1] = c->authority_selector_md;
925 cb(user_data, md_array, 2, GRPC_CREDENTIALS_OK);
926}
927
928static grpc_credentials_vtable iam_vtable = {
929 iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
930 iam_get_request_metadata};
931
932grpc_credentials *grpc_iam_credentials_create(const char *token,
933 const char *authority_selector) {
934 grpc_iam_credentials *c;
935 GPR_ASSERT(token != NULL);
936 GPR_ASSERT(authority_selector != NULL);
937 c = gpr_malloc(sizeof(grpc_iam_credentials));
938 memset(c, 0, sizeof(grpc_iam_credentials));
939 c->base.type = GRPC_CREDENTIALS_TYPE_IAM;
940 c->base.vtable = &iam_vtable;
941 gpr_ref_init(&c->base.refcount, 1);
942 c->md_ctx = grpc_mdctx_create();
943 c->token_md = grpc_mdelem_from_strings(
944 c->md_ctx, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
945 c->authority_selector_md = grpc_mdelem_from_strings(
946 c->md_ctx, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
947 return &c->base;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800948}
949
950/* -- Default credentials TODO(jboeuf). -- */
951
952grpc_credentials *grpc_default_credentials_create(void) { return NULL; }