blob: 969a97369cbb0624400df834507871191c317e51 [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"
37#include "src/core/surface/surface_em.h"
38#include <grpc/support/alloc.h>
39#include <grpc/support/log.h>
40#include <grpc/support/string.h>
41#include <grpc/support/sync.h>
42#include <grpc/support/time.h>
43
44#include "third_party/cJSON/cJSON.h"
45
46#include <string.h>
47#include <stdio.h>
48
49/* -- Constants. -- */
50
51#define GRPC_COMPUTE_ENGINE_TOKEN_REFRESH_THRESHOLD_SECS 60
52#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata"
53#define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \
54 "computeMetadata/v1/instance/service-accounts/default/token"
55#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization"
56
57/* -- Common. -- */
58
59typedef struct {
60 grpc_credentials *creds;
61 grpc_credentials_metadata_cb cb;
62 void *user_data;
63} grpc_credentials_metadata_request;
64
65static grpc_credentials_metadata_request *
66grpc_credentials_metadata_request_create(grpc_credentials *creds,
67 grpc_credentials_metadata_cb cb,
68 void *user_data) {
69 grpc_credentials_metadata_request *r =
70 gpr_malloc(sizeof(grpc_credentials_metadata_request));
71 r->creds = grpc_credentials_ref(creds);
72 r->cb = cb;
73 r->user_data = user_data;
74 return r;
75}
76
77static void grpc_credentials_metadata_request_destroy(
78 grpc_credentials_metadata_request *r) {
79 grpc_credentials_unref(r->creds);
80 gpr_free(r);
81}
82
83grpc_credentials *grpc_credentials_ref(grpc_credentials *creds) {
84 if (creds == NULL) return NULL;
85 gpr_ref(&creds->refcount);
86 return creds;
87}
88
89void grpc_credentials_unref(grpc_credentials *creds) {
90 if (creds == NULL) return;
91 if (gpr_unref(&creds->refcount)) creds->vtable->destroy(creds);
92}
93
94void grpc_credentials_release(grpc_credentials *creds) {
95 grpc_credentials_unref(creds);
96}
97
98int grpc_credentials_has_request_metadata(grpc_credentials *creds) {
99 if (creds == NULL) return 0;
100 return creds->vtable->has_request_metadata(creds);
101}
102
103int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) {
104 if (creds == NULL) return 0;
105 return creds->vtable->has_request_metadata_only(creds);
106}
107
108void grpc_credentials_get_request_metadata(grpc_credentials *creds,
109 grpc_credentials_metadata_cb cb,
110 void *user_data) {
111 if (creds == NULL || !grpc_credentials_has_request_metadata(creds)) return;
112 creds->vtable->get_request_metadata(creds, cb, user_data);
113}
114
115void grpc_server_credentials_release(grpc_server_credentials *creds) {
116 if (creds == NULL) return;
117 creds->vtable->destroy(creds);
118}
119
120/* -- Ssl credentials. -- */
121
122typedef struct {
123 grpc_credentials base;
124 grpc_ssl_config config;
125} grpc_ssl_credentials;
126
127typedef struct {
128 grpc_server_credentials base;
129 grpc_ssl_config config;
130} grpc_ssl_server_credentials;
131
132static void ssl_destroy(grpc_credentials *creds) {
133 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
134 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
135 if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
136 if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
137 gpr_free(creds);
138}
139
140static void ssl_server_destroy(grpc_server_credentials *creds) {
141 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
142 if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
143 if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
144 if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
145 gpr_free(creds);
146}
147
148static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; }
149
150static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
151 return 0;
152}
153
154static grpc_credentials_vtable ssl_vtable = {
155 ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL};
156
157static grpc_server_credentials_vtable ssl_server_vtable = {ssl_server_destroy};
158
159const grpc_ssl_config *grpc_ssl_credentials_get_config(
160 const grpc_credentials *creds) {
161 if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
162 return NULL;
163 } else {
164 grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
165 return &c->config;
166 }
167}
168
169const grpc_ssl_config *grpc_ssl_server_credentials_get_config(
170 const grpc_server_credentials *creds) {
171 if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
172 return NULL;
173 } else {
174 grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
175 return &c->config;
176 }
177}
178
179static void ssl_build_config(const unsigned char *pem_root_certs,
180 size_t pem_root_certs_size,
181 const unsigned char *pem_private_key,
182 size_t pem_private_key_size,
183 const unsigned char *pem_cert_chain,
184 size_t pem_cert_chain_size,
185 grpc_ssl_config *config) {
186 if (pem_root_certs != NULL) {
187 config->pem_root_certs = gpr_malloc(pem_root_certs_size);
188 memcpy(config->pem_root_certs, pem_root_certs, pem_root_certs_size);
189 config->pem_root_certs_size = pem_root_certs_size;
190 }
191 if (pem_private_key != NULL) {
192 config->pem_private_key = gpr_malloc(pem_private_key_size);
193 memcpy(config->pem_private_key, pem_private_key, pem_private_key_size);
194 config->pem_private_key_size = pem_private_key_size;
195 }
196 if (pem_cert_chain != NULL) {
197 config->pem_cert_chain = gpr_malloc(pem_cert_chain_size);
198 memcpy(config->pem_cert_chain, pem_cert_chain, pem_cert_chain_size);
199 config->pem_cert_chain_size = pem_cert_chain_size;
200 }
201}
202
203grpc_credentials *grpc_ssl_credentials_create(
204 const unsigned char *pem_root_certs, size_t pem_root_certs_size,
205 const unsigned char *pem_private_key, size_t pem_private_key_size,
206 const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) {
207 grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
208 memset(c, 0, sizeof(grpc_ssl_credentials));
209 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
210 c->base.vtable = &ssl_vtable;
211 gpr_ref_init(&c->base.refcount, 1);
212 ssl_build_config(pem_root_certs, pem_root_certs_size, pem_private_key,
213 pem_private_key_size, pem_cert_chain, pem_cert_chain_size,
214 &c->config);
215 return &c->base;
216}
217
218grpc_server_credentials *grpc_ssl_server_credentials_create(
219 const unsigned char *pem_root_certs, size_t pem_root_certs_size,
220 const unsigned char *pem_private_key, size_t pem_private_key_size,
221 const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) {
222 grpc_ssl_server_credentials *c =
223 gpr_malloc(sizeof(grpc_ssl_server_credentials));
224 memset(c, 0, sizeof(grpc_ssl_server_credentials));
225 c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
226 c->base.vtable = &ssl_server_vtable;
227 ssl_build_config(pem_root_certs, pem_root_certs_size, pem_private_key,
228 pem_private_key_size, pem_cert_chain, pem_cert_chain_size,
229 &c->config);
230 return &c->base;
231}
232
233/* -- ComputeEngine credentials. -- */
234
235typedef struct {
236 grpc_credentials base;
237 gpr_mu mu;
238 grpc_mdctx *md_ctx;
239 grpc_mdelem *access_token_md;
240 gpr_timespec token_expiration;
241} grpc_compute_engine_credentials;
242
243static void compute_engine_destroy(grpc_credentials *creds) {
244 grpc_compute_engine_credentials *c = (grpc_compute_engine_credentials *)creds;
245 if (c->access_token_md != NULL) {
246 grpc_mdelem_unref(c->access_token_md);
247 }
248 gpr_mu_destroy(&c->mu);
249 grpc_mdctx_orphan(c->md_ctx);
250 gpr_free(c);
251}
252
253static int compute_engine_has_request_metadata(const grpc_credentials *creds) {
254 return 1;
255}
256
257static int compute_engine_has_request_metadata_only(
258 const grpc_credentials *creds) {
259 return 1;
260}
261
262grpc_credentials_status grpc_compute_engine_credentials_parse_server_response(
263 const grpc_httpcli_response *response, grpc_mdctx *ctx,
264 grpc_mdelem **token_elem, gpr_timespec *token_lifetime) {
265 char *null_terminated_body = NULL;
266 char *new_access_token = NULL;
267 grpc_credentials_status status = GRPC_CREDENTIALS_OK;
268 cJSON *json = NULL;
269
270 if (response->status != 200) {
271 gpr_log(GPR_ERROR, "Call to metadata server ended with error %d",
272 response->status);
273 status = GRPC_CREDENTIALS_ERROR;
274 goto end;
275 } else {
276 cJSON *access_token = NULL;
277 cJSON *token_type = NULL;
278 cJSON *expires_in = NULL;
279 size_t new_access_token_size = 0;
280 null_terminated_body = gpr_malloc(response->body_length + 1);
281 null_terminated_body[response->body_length] = '\0';
282 memcpy(null_terminated_body, response->body, response->body_length);
283 json = cJSON_Parse(null_terminated_body);
284 if (json == NULL) {
285 gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
286 status = GRPC_CREDENTIALS_ERROR;
287 goto end;
288 }
289 if (json->type != cJSON_Object) {
290 gpr_log(GPR_ERROR, "Response should be a JSON object");
291 status = GRPC_CREDENTIALS_ERROR;
292 goto end;
293 }
294 access_token = cJSON_GetObjectItem(json, "access_token");
295 if (access_token == NULL || access_token->type != cJSON_String) {
296 gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
297 status = GRPC_CREDENTIALS_ERROR;
298 goto end;
299 }
300 token_type = cJSON_GetObjectItem(json, "token_type");
301 if (token_type == NULL || token_type->type != cJSON_String) {
302 gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
303 status = GRPC_CREDENTIALS_ERROR;
304 goto end;
305 }
306 expires_in = cJSON_GetObjectItem(json, "expires_in");
307 if (expires_in == NULL || expires_in->type != cJSON_Number) {
308 gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
309 status = GRPC_CREDENTIALS_ERROR;
310 goto end;
311 }
312 new_access_token_size = strlen(token_type->valuestring) + 1 +
313 strlen(access_token->valuestring) + 1;
314 new_access_token = gpr_malloc(new_access_token_size);
315 /* C89 does not have snprintf :(. */
316 sprintf(new_access_token, "%s %s", token_type->valuestring,
317 access_token->valuestring);
318 token_lifetime->tv_sec = expires_in->valueint;
319 token_lifetime->tv_nsec = 0;
320 if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
321 *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
322 new_access_token);
323 status = GRPC_CREDENTIALS_OK;
324 }
325
326end:
327 if (status != GRPC_CREDENTIALS_OK && (*token_elem != NULL)) {
328 grpc_mdelem_unref(*token_elem);
329 *token_elem = NULL;
330 }
331 if (null_terminated_body != NULL) gpr_free(null_terminated_body);
332 if (new_access_token != NULL) gpr_free(new_access_token);
333 if (json != NULL) cJSON_Delete(json);
334 return status;
335}
336
337static void on_compute_engine_token_response(
338 void *user_data, const grpc_httpcli_response *response) {
339 grpc_credentials_metadata_request *r =
340 (grpc_credentials_metadata_request *)user_data;
341 grpc_compute_engine_credentials *c =
342 (grpc_compute_engine_credentials *)r->creds;
343 gpr_timespec token_lifetime;
344 grpc_credentials_status status;
345
346 gpr_mu_lock(&c->mu);
347 status = grpc_compute_engine_credentials_parse_server_response(
348 response, c->md_ctx, &c->access_token_md, &token_lifetime);
349 if (status == GRPC_CREDENTIALS_OK) {
350 c->token_expiration = gpr_time_add(gpr_now(), token_lifetime);
351 r->cb(r->user_data, &c->access_token_md, 1, status);
352 } else {
353 c->token_expiration = gpr_inf_past;
354 r->cb(r->user_data, NULL, 0, status);
355 }
356 gpr_mu_unlock(&c->mu);
357 grpc_credentials_metadata_request_destroy(r);
358}
359
360static void compute_engine_get_request_metadata(grpc_credentials *creds,
361 grpc_credentials_metadata_cb cb,
362 void *user_data) {
363 grpc_compute_engine_credentials *c = (grpc_compute_engine_credentials *)creds;
364 gpr_timespec refresh_threshold = {
365 GRPC_COMPUTE_ENGINE_TOKEN_REFRESH_THRESHOLD_SECS, 0};
366
367 gpr_mu_lock(&c->mu);
368 if (c->access_token_md == NULL ||
369 (gpr_time_cmp(gpr_time_sub(gpr_now(), c->token_expiration),
370 refresh_threshold) < 0)) {
371 grpc_httpcli_header header = {"Metadata-Flavor", "Google"};
372 grpc_httpcli_request request;
373 request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
374 request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
375 request.hdr_count = 1;
376 request.hdrs = &header;
377 grpc_httpcli_get(
378 &request, gpr_time_add(gpr_now(), refresh_threshold), grpc_surface_em(),
379 on_compute_engine_token_response,
380 grpc_credentials_metadata_request_create(creds, cb, user_data));
381 } else {
382 cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
383 }
384 gpr_mu_unlock(&c->mu);
385}
386
387static grpc_credentials_vtable compute_engine_vtable = {
388 compute_engine_destroy, compute_engine_has_request_metadata,
389 compute_engine_has_request_metadata_only,
390 compute_engine_get_request_metadata};
391
392grpc_credentials *grpc_compute_engine_credentials_create(void) {
393 grpc_compute_engine_credentials *c =
394 gpr_malloc(sizeof(grpc_compute_engine_credentials));
395 memset(c, 0, sizeof(grpc_compute_engine_credentials));
396 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
397 c->base.vtable = &compute_engine_vtable;
398 gpr_ref_init(&c->base.refcount, 1);
399 gpr_mu_init(&c->mu);
400 c->md_ctx = grpc_mdctx_create();
401 c->token_expiration = gpr_inf_past;
402 return &c->base;
403}
404
405/* -- Fake Oauth2 credentials. -- */
406
407typedef struct {
408 grpc_credentials base;
409 grpc_mdctx *md_ctx;
410 grpc_mdelem *access_token_md;
411 int is_async;
412} grpc_fake_oauth2_credentials;
413
414static void fake_oauth2_destroy(grpc_credentials *creds) {
415 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
416 if (c->access_token_md != NULL) {
417 grpc_mdelem_unref(c->access_token_md);
418 }
419 grpc_mdctx_orphan(c->md_ctx);
420 gpr_free(c);
421}
422
423static int fake_oauth2_has_request_metadata(const grpc_credentials *creds) {
424 return 1;
425}
426
427static int fake_oauth2_has_request_metadata_only(
428 const grpc_credentials *creds) {
429 return 1;
430}
431
432void on_simulated_token_fetch_done(void *user_data, grpc_em_cb_status status) {
433 grpc_credentials_metadata_request *r =
434 (grpc_credentials_metadata_request *)user_data;
435 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)r->creds;
436 GPR_ASSERT(status == GRPC_CALLBACK_SUCCESS);
437 r->cb(r->user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
438 grpc_credentials_metadata_request_destroy(r);
439}
440
441static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
442 grpc_credentials_metadata_cb cb,
443 void *user_data) {
444 grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
445
446 if (c->is_async) {
447 GPR_ASSERT(grpc_em_add_callback(grpc_surface_em(),
448 on_simulated_token_fetch_done,
449 grpc_credentials_metadata_request_create(
450 creds, cb, user_data)) == GRPC_EM_OK);
451 } else {
452 cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
453 }
454}
455
456static grpc_credentials_vtable fake_oauth2_vtable = {
457 fake_oauth2_destroy, fake_oauth2_has_request_metadata,
458 fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata};
459
460grpc_credentials *grpc_fake_oauth2_credentials_create(
461 const char *token_md_value, int is_async) {
462 grpc_fake_oauth2_credentials *c =
463 gpr_malloc(sizeof(grpc_fake_oauth2_credentials));
464 memset(c, 0, sizeof(grpc_fake_oauth2_credentials));
465 c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
466 c->base.vtable = &fake_oauth2_vtable;
467 gpr_ref_init(&c->base.refcount, 1);
468 c->md_ctx = grpc_mdctx_create();
469 c->access_token_md = grpc_mdelem_from_strings(
470 c->md_ctx, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
471 c->is_async = is_async;
472 return &c->base;
473}
474
475/* -- Fake transport security credentials. -- */
476
477static void fake_transport_security_credentials_destroy(
478 grpc_credentials *creds) {
479 gpr_free(creds);
480}
481
482static void fake_transport_security_server_credentials_destroy(
483 grpc_server_credentials *creds) {
484 gpr_free(creds);
485}
486
487static int fake_transport_security_has_request_metadata(
488 const grpc_credentials *creds) {
489 return 0;
490}
491
492static int fake_transport_security_has_request_metadata_only(
493 const grpc_credentials *creds) {
494 return 0;
495}
496
497static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
498 fake_transport_security_credentials_destroy,
499 fake_transport_security_has_request_metadata,
500 fake_transport_security_has_request_metadata_only, NULL};
501
502static grpc_server_credentials_vtable
503 fake_transport_security_server_credentials_vtable = {
504 fake_transport_security_server_credentials_destroy};
505
506grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
507 grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
508 memset(c, 0, sizeof(grpc_credentials));
509 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
510 c->vtable = &fake_transport_security_credentials_vtable;
511 gpr_ref_init(&c->refcount, 1);
512 return c;
513}
514
515grpc_server_credentials *
516grpc_fake_transport_security_server_credentials_create() {
517 grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
518 memset(c, 0, sizeof(grpc_server_credentials));
519 c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
520 c->vtable = &fake_transport_security_server_credentials_vtable;
521 return c;
522}
523
524
525/* -- Composite credentials TODO(jboeuf). -- */
526
527grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
528 grpc_credentials *creds2) {
529 return NULL;
530}
531
532/* -- Default credentials TODO(jboeuf). -- */
533
534grpc_credentials *grpc_default_credentials_create(void) { return NULL; }