aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/security/credentials.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/security/credentials.c')
-rw-r--r--src/core/security/credentials.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
new file mode 100644
index 0000000000..969a97369c
--- /dev/null
+++ b/src/core/security/credentials.c
@@ -0,0 +1,534 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/security/credentials.h"
+
+#include "src/core/httpcli/httpcli.h"
+#include "src/core/surface/surface_em.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "third_party/cJSON/cJSON.h"
+
+#include <string.h>
+#include <stdio.h>
+
+/* -- Constants. -- */
+
+#define GRPC_COMPUTE_ENGINE_TOKEN_REFRESH_THRESHOLD_SECS 60
+#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata"
+#define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \
+ "computeMetadata/v1/instance/service-accounts/default/token"
+#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization"
+
+/* -- Common. -- */
+
+typedef struct {
+ grpc_credentials *creds;
+ grpc_credentials_metadata_cb cb;
+ void *user_data;
+} grpc_credentials_metadata_request;
+
+static grpc_credentials_metadata_request *
+grpc_credentials_metadata_request_create(grpc_credentials *creds,
+ grpc_credentials_metadata_cb cb,
+ void *user_data) {
+ grpc_credentials_metadata_request *r =
+ gpr_malloc(sizeof(grpc_credentials_metadata_request));
+ r->creds = grpc_credentials_ref(creds);
+ r->cb = cb;
+ r->user_data = user_data;
+ return r;
+}
+
+static void grpc_credentials_metadata_request_destroy(
+ grpc_credentials_metadata_request *r) {
+ grpc_credentials_unref(r->creds);
+ gpr_free(r);
+}
+
+grpc_credentials *grpc_credentials_ref(grpc_credentials *creds) {
+ if (creds == NULL) return NULL;
+ gpr_ref(&creds->refcount);
+ return creds;
+}
+
+void grpc_credentials_unref(grpc_credentials *creds) {
+ if (creds == NULL) return;
+ if (gpr_unref(&creds->refcount)) creds->vtable->destroy(creds);
+}
+
+void grpc_credentials_release(grpc_credentials *creds) {
+ grpc_credentials_unref(creds);
+}
+
+int grpc_credentials_has_request_metadata(grpc_credentials *creds) {
+ if (creds == NULL) return 0;
+ return creds->vtable->has_request_metadata(creds);
+}
+
+int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) {
+ if (creds == NULL) return 0;
+ return creds->vtable->has_request_metadata_only(creds);
+}
+
+void grpc_credentials_get_request_metadata(grpc_credentials *creds,
+ grpc_credentials_metadata_cb cb,
+ void *user_data) {
+ if (creds == NULL || !grpc_credentials_has_request_metadata(creds)) return;
+ creds->vtable->get_request_metadata(creds, cb, user_data);
+}
+
+void grpc_server_credentials_release(grpc_server_credentials *creds) {
+ if (creds == NULL) return;
+ creds->vtable->destroy(creds);
+}
+
+/* -- Ssl credentials. -- */
+
+typedef struct {
+ grpc_credentials base;
+ grpc_ssl_config config;
+} grpc_ssl_credentials;
+
+typedef struct {
+ grpc_server_credentials base;
+ grpc_ssl_config config;
+} grpc_ssl_server_credentials;
+
+static void ssl_destroy(grpc_credentials *creds) {
+ grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
+ if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
+ if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
+ if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
+ gpr_free(creds);
+}
+
+static void ssl_server_destroy(grpc_server_credentials *creds) {
+ grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
+ if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
+ if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
+ if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
+ gpr_free(creds);
+}
+
+static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; }
+
+static int ssl_has_request_metadata_only(const grpc_credentials *creds) {
+ return 0;
+}
+
+static grpc_credentials_vtable ssl_vtable = {
+ ssl_destroy, ssl_has_request_metadata, ssl_has_request_metadata_only, NULL};
+
+static grpc_server_credentials_vtable ssl_server_vtable = {ssl_server_destroy};
+
+const grpc_ssl_config *grpc_ssl_credentials_get_config(
+ const grpc_credentials *creds) {
+ if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
+ return NULL;
+ } else {
+ grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
+ return &c->config;
+ }
+}
+
+const grpc_ssl_config *grpc_ssl_server_credentials_get_config(
+ const grpc_server_credentials *creds) {
+ if (creds == NULL || strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
+ return NULL;
+ } else {
+ grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
+ return &c->config;
+ }
+}
+
+static void ssl_build_config(const unsigned char *pem_root_certs,
+ size_t pem_root_certs_size,
+ const unsigned char *pem_private_key,
+ size_t pem_private_key_size,
+ const unsigned char *pem_cert_chain,
+ size_t pem_cert_chain_size,
+ grpc_ssl_config *config) {
+ if (pem_root_certs != NULL) {
+ config->pem_root_certs = gpr_malloc(pem_root_certs_size);
+ memcpy(config->pem_root_certs, pem_root_certs, pem_root_certs_size);
+ config->pem_root_certs_size = pem_root_certs_size;
+ }
+ if (pem_private_key != NULL) {
+ config->pem_private_key = gpr_malloc(pem_private_key_size);
+ memcpy(config->pem_private_key, pem_private_key, pem_private_key_size);
+ config->pem_private_key_size = pem_private_key_size;
+ }
+ if (pem_cert_chain != NULL) {
+ config->pem_cert_chain = gpr_malloc(pem_cert_chain_size);
+ memcpy(config->pem_cert_chain, pem_cert_chain, pem_cert_chain_size);
+ config->pem_cert_chain_size = pem_cert_chain_size;
+ }
+}
+
+grpc_credentials *grpc_ssl_credentials_create(
+ const unsigned char *pem_root_certs, size_t pem_root_certs_size,
+ const unsigned char *pem_private_key, size_t pem_private_key_size,
+ const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) {
+ grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
+ memset(c, 0, sizeof(grpc_ssl_credentials));
+ c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
+ c->base.vtable = &ssl_vtable;
+ gpr_ref_init(&c->base.refcount, 1);
+ ssl_build_config(pem_root_certs, pem_root_certs_size, pem_private_key,
+ pem_private_key_size, pem_cert_chain, pem_cert_chain_size,
+ &c->config);
+ return &c->base;
+}
+
+grpc_server_credentials *grpc_ssl_server_credentials_create(
+ const unsigned char *pem_root_certs, size_t pem_root_certs_size,
+ const unsigned char *pem_private_key, size_t pem_private_key_size,
+ const unsigned char *pem_cert_chain, size_t pem_cert_chain_size) {
+ grpc_ssl_server_credentials *c =
+ gpr_malloc(sizeof(grpc_ssl_server_credentials));
+ memset(c, 0, sizeof(grpc_ssl_server_credentials));
+ c->base.type = GRPC_CREDENTIALS_TYPE_SSL;
+ c->base.vtable = &ssl_server_vtable;
+ ssl_build_config(pem_root_certs, pem_root_certs_size, pem_private_key,
+ pem_private_key_size, pem_cert_chain, pem_cert_chain_size,
+ &c->config);
+ return &c->base;
+}
+
+/* -- ComputeEngine credentials. -- */
+
+typedef struct {
+ grpc_credentials base;
+ gpr_mu mu;
+ grpc_mdctx *md_ctx;
+ grpc_mdelem *access_token_md;
+ gpr_timespec token_expiration;
+} grpc_compute_engine_credentials;
+
+static void compute_engine_destroy(grpc_credentials *creds) {
+ grpc_compute_engine_credentials *c = (grpc_compute_engine_credentials *)creds;
+ if (c->access_token_md != NULL) {
+ grpc_mdelem_unref(c->access_token_md);
+ }
+ gpr_mu_destroy(&c->mu);
+ grpc_mdctx_orphan(c->md_ctx);
+ gpr_free(c);
+}
+
+static int compute_engine_has_request_metadata(const grpc_credentials *creds) {
+ return 1;
+}
+
+static int compute_engine_has_request_metadata_only(
+ const grpc_credentials *creds) {
+ return 1;
+}
+
+grpc_credentials_status grpc_compute_engine_credentials_parse_server_response(
+ const grpc_httpcli_response *response, grpc_mdctx *ctx,
+ grpc_mdelem **token_elem, gpr_timespec *token_lifetime) {
+ char *null_terminated_body = NULL;
+ char *new_access_token = NULL;
+ grpc_credentials_status status = GRPC_CREDENTIALS_OK;
+ cJSON *json = NULL;
+
+ if (response->status != 200) {
+ gpr_log(GPR_ERROR, "Call to metadata server ended with error %d",
+ response->status);
+ status = GRPC_CREDENTIALS_ERROR;
+ goto end;
+ } else {
+ cJSON *access_token = NULL;
+ cJSON *token_type = NULL;
+ cJSON *expires_in = NULL;
+ size_t new_access_token_size = 0;
+ null_terminated_body = gpr_malloc(response->body_length + 1);
+ null_terminated_body[response->body_length] = '\0';
+ memcpy(null_terminated_body, response->body, response->body_length);
+ json = cJSON_Parse(null_terminated_body);
+ if (json == NULL) {
+ gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
+ status = GRPC_CREDENTIALS_ERROR;
+ goto end;
+ }
+ if (json->type != cJSON_Object) {
+ gpr_log(GPR_ERROR, "Response should be a JSON object");
+ status = GRPC_CREDENTIALS_ERROR;
+ goto end;
+ }
+ access_token = cJSON_GetObjectItem(json, "access_token");
+ if (access_token == NULL || access_token->type != cJSON_String) {
+ gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
+ status = GRPC_CREDENTIALS_ERROR;
+ goto end;
+ }
+ token_type = cJSON_GetObjectItem(json, "token_type");
+ if (token_type == NULL || token_type->type != cJSON_String) {
+ gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
+ status = GRPC_CREDENTIALS_ERROR;
+ goto end;
+ }
+ expires_in = cJSON_GetObjectItem(json, "expires_in");
+ if (expires_in == NULL || expires_in->type != cJSON_Number) {
+ gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
+ status = GRPC_CREDENTIALS_ERROR;
+ goto end;
+ }
+ new_access_token_size = strlen(token_type->valuestring) + 1 +
+ strlen(access_token->valuestring) + 1;
+ new_access_token = gpr_malloc(new_access_token_size);
+ /* C89 does not have snprintf :(. */
+ sprintf(new_access_token, "%s %s", token_type->valuestring,
+ access_token->valuestring);
+ token_lifetime->tv_sec = expires_in->valueint;
+ token_lifetime->tv_nsec = 0;
+ if (*token_elem != NULL) grpc_mdelem_unref(*token_elem);
+ *token_elem = grpc_mdelem_from_strings(ctx, GRPC_AUTHORIZATION_METADATA_KEY,
+ new_access_token);
+ status = GRPC_CREDENTIALS_OK;
+ }
+
+end:
+ if (status != GRPC_CREDENTIALS_OK && (*token_elem != NULL)) {
+ grpc_mdelem_unref(*token_elem);
+ *token_elem = NULL;
+ }
+ if (null_terminated_body != NULL) gpr_free(null_terminated_body);
+ if (new_access_token != NULL) gpr_free(new_access_token);
+ if (json != NULL) cJSON_Delete(json);
+ return status;
+}
+
+static void on_compute_engine_token_response(
+ void *user_data, const grpc_httpcli_response *response) {
+ grpc_credentials_metadata_request *r =
+ (grpc_credentials_metadata_request *)user_data;
+ grpc_compute_engine_credentials *c =
+ (grpc_compute_engine_credentials *)r->creds;
+ gpr_timespec token_lifetime;
+ grpc_credentials_status status;
+
+ gpr_mu_lock(&c->mu);
+ status = grpc_compute_engine_credentials_parse_server_response(
+ response, c->md_ctx, &c->access_token_md, &token_lifetime);
+ if (status == GRPC_CREDENTIALS_OK) {
+ c->token_expiration = gpr_time_add(gpr_now(), token_lifetime);
+ r->cb(r->user_data, &c->access_token_md, 1, status);
+ } else {
+ c->token_expiration = gpr_inf_past;
+ r->cb(r->user_data, NULL, 0, status);
+ }
+ gpr_mu_unlock(&c->mu);
+ grpc_credentials_metadata_request_destroy(r);
+}
+
+static void compute_engine_get_request_metadata(grpc_credentials *creds,
+ grpc_credentials_metadata_cb cb,
+ void *user_data) {
+ grpc_compute_engine_credentials *c = (grpc_compute_engine_credentials *)creds;
+ gpr_timespec refresh_threshold = {
+ GRPC_COMPUTE_ENGINE_TOKEN_REFRESH_THRESHOLD_SECS, 0};
+
+ gpr_mu_lock(&c->mu);
+ if (c->access_token_md == NULL ||
+ (gpr_time_cmp(gpr_time_sub(gpr_now(), c->token_expiration),
+ refresh_threshold) < 0)) {
+ grpc_httpcli_header header = {"Metadata-Flavor", "Google"};
+ grpc_httpcli_request request;
+ request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
+ request.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
+ request.hdr_count = 1;
+ request.hdrs = &header;
+ grpc_httpcli_get(
+ &request, gpr_time_add(gpr_now(), refresh_threshold), grpc_surface_em(),
+ on_compute_engine_token_response,
+ grpc_credentials_metadata_request_create(creds, cb, user_data));
+ } else {
+ cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
+ }
+ gpr_mu_unlock(&c->mu);
+}
+
+static grpc_credentials_vtable compute_engine_vtable = {
+ compute_engine_destroy, compute_engine_has_request_metadata,
+ compute_engine_has_request_metadata_only,
+ compute_engine_get_request_metadata};
+
+grpc_credentials *grpc_compute_engine_credentials_create(void) {
+ grpc_compute_engine_credentials *c =
+ gpr_malloc(sizeof(grpc_compute_engine_credentials));
+ memset(c, 0, sizeof(grpc_compute_engine_credentials));
+ c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
+ c->base.vtable = &compute_engine_vtable;
+ gpr_ref_init(&c->base.refcount, 1);
+ gpr_mu_init(&c->mu);
+ c->md_ctx = grpc_mdctx_create();
+ c->token_expiration = gpr_inf_past;
+ return &c->base;
+}
+
+/* -- Fake Oauth2 credentials. -- */
+
+typedef struct {
+ grpc_credentials base;
+ grpc_mdctx *md_ctx;
+ grpc_mdelem *access_token_md;
+ int is_async;
+} grpc_fake_oauth2_credentials;
+
+static void fake_oauth2_destroy(grpc_credentials *creds) {
+ grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
+ if (c->access_token_md != NULL) {
+ grpc_mdelem_unref(c->access_token_md);
+ }
+ grpc_mdctx_orphan(c->md_ctx);
+ gpr_free(c);
+}
+
+static int fake_oauth2_has_request_metadata(const grpc_credentials *creds) {
+ return 1;
+}
+
+static int fake_oauth2_has_request_metadata_only(
+ const grpc_credentials *creds) {
+ return 1;
+}
+
+void on_simulated_token_fetch_done(void *user_data, grpc_em_cb_status status) {
+ grpc_credentials_metadata_request *r =
+ (grpc_credentials_metadata_request *)user_data;
+ grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)r->creds;
+ GPR_ASSERT(status == GRPC_CALLBACK_SUCCESS);
+ r->cb(r->user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
+ grpc_credentials_metadata_request_destroy(r);
+}
+
+static void fake_oauth2_get_request_metadata(grpc_credentials *creds,
+ grpc_credentials_metadata_cb cb,
+ void *user_data) {
+ grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
+
+ if (c->is_async) {
+ GPR_ASSERT(grpc_em_add_callback(grpc_surface_em(),
+ on_simulated_token_fetch_done,
+ grpc_credentials_metadata_request_create(
+ creds, cb, user_data)) == GRPC_EM_OK);
+ } else {
+ cb(user_data, &c->access_token_md, 1, GRPC_CREDENTIALS_OK);
+ }
+}
+
+static grpc_credentials_vtable fake_oauth2_vtable = {
+ fake_oauth2_destroy, fake_oauth2_has_request_metadata,
+ fake_oauth2_has_request_metadata_only, fake_oauth2_get_request_metadata};
+
+grpc_credentials *grpc_fake_oauth2_credentials_create(
+ const char *token_md_value, int is_async) {
+ grpc_fake_oauth2_credentials *c =
+ gpr_malloc(sizeof(grpc_fake_oauth2_credentials));
+ memset(c, 0, sizeof(grpc_fake_oauth2_credentials));
+ c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
+ c->base.vtable = &fake_oauth2_vtable;
+ gpr_ref_init(&c->base.refcount, 1);
+ c->md_ctx = grpc_mdctx_create();
+ c->access_token_md = grpc_mdelem_from_strings(
+ c->md_ctx, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
+ c->is_async = is_async;
+ return &c->base;
+}
+
+/* -- Fake transport security credentials. -- */
+
+static void fake_transport_security_credentials_destroy(
+ grpc_credentials *creds) {
+ gpr_free(creds);
+}
+
+static void fake_transport_security_server_credentials_destroy(
+ grpc_server_credentials *creds) {
+ gpr_free(creds);
+}
+
+static int fake_transport_security_has_request_metadata(
+ const grpc_credentials *creds) {
+ return 0;
+}
+
+static int fake_transport_security_has_request_metadata_only(
+ const grpc_credentials *creds) {
+ return 0;
+}
+
+static grpc_credentials_vtable fake_transport_security_credentials_vtable = {
+ fake_transport_security_credentials_destroy,
+ fake_transport_security_has_request_metadata,
+ fake_transport_security_has_request_metadata_only, NULL};
+
+static grpc_server_credentials_vtable
+ fake_transport_security_server_credentials_vtable = {
+ fake_transport_security_server_credentials_destroy};
+
+grpc_credentials *grpc_fake_transport_security_credentials_create(void) {
+ grpc_credentials *c = gpr_malloc(sizeof(grpc_credentials));
+ memset(c, 0, sizeof(grpc_credentials));
+ c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
+ c->vtable = &fake_transport_security_credentials_vtable;
+ gpr_ref_init(&c->refcount, 1);
+ return c;
+}
+
+grpc_server_credentials *
+grpc_fake_transport_security_server_credentials_create() {
+ grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
+ memset(c, 0, sizeof(grpc_server_credentials));
+ c->type = GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
+ c->vtable = &fake_transport_security_server_credentials_vtable;
+ return c;
+}
+
+
+/* -- Composite credentials TODO(jboeuf). -- */
+
+grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
+ grpc_credentials *creds2) {
+ return NULL;
+}
+
+/* -- Default credentials TODO(jboeuf). -- */
+
+grpc_credentials *grpc_default_credentials_create(void) { return NULL; }